From deb145bf21d35066736c9edfbe379c308d7d602a Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 19 Sep 2023 10:00:54 +0200 Subject: [PATCH 001/129] Create workspace.xml --- .idea/workspace.xml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..502532f6 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + 1695030594010 + + + + \ No newline at end of file From 424e2947e4c3ae7f071cdecb2233a5a8c266fa7b Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 19 Sep 2023 10:01:23 +0200 Subject: [PATCH 002/129] fix --- .idea/.gitignore | 3 +++ .idea/educado-backend.iml | 9 +++++++++ .idea/misc.xml | 6 ++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ 5 files changed, 32 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/educado-backend.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/educado-backend.iml b/.idea/educado-backend.iml new file mode 100644 index 00000000..d6ebd480 --- /dev/null +++ b/.idea/educado-backend.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..639900d1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..fe7a66f7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From e3c9e51551c63b8af3f58b5b43c88cc65bc50d73 Mon Sep 17 00:00:00 2001 From: ITPini Date: Thu, 21 Sep 2023 13:26:32 +0200 Subject: [PATCH 003/129] Renamed to config/.env --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 549fa3db..5e4f90a2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ config/dev.js *.DS_Store package-lock.json dev.js -.env +config/.env config/gcp_service.json From 446c4b0d641d39be66ac9386f27341b441c2b8db Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 21 Sep 2023 18:40:14 +0200 Subject: [PATCH 004/129] back-end subscriptions --- .gitignore | 2 +- config/keys.js | 4 ++-- models/User.js | 3 ++- routes/courseRoutes.js | 53 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 549fa3db..5e4f90a2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ config/dev.js *.DS_Store package-lock.json dev.js -.env +config/.env config/gcp_service.json diff --git a/config/keys.js b/config/keys.js index ca317ac5..d8a8fbc0 100644 --- a/config/keys.js +++ b/config/keys.js @@ -2,9 +2,9 @@ const dotenv = require("dotenv"); // Load environment variables based on NODE_ENV if (process.env.NODE_ENV === "production") { - dotenv.config({ path: "./config/.env.prod" }); + dotenv.config({ path: ".env.prod" }); } else { - dotenv.config({ path: "./config/.env" }); + dotenv.config({ path: "config/.env" }); } // Access the environment variables diff --git a/models/User.js b/models/User.js index 7a8e7a17..46bb51ba 100644 --- a/models/User.js +++ b/models/User.js @@ -8,7 +8,8 @@ const userSchema = new Schema({ email: String, password: String, joinedAt: Date, - modifiedAt: Date + modifiedAt: Date, + _subscriptions: [{ type: Schema.Types.ObjectId, ref: "Course" }] }); const UserModel = mongoose.model("users", userSchema); diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 883f692d..43b4ad47 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -8,6 +8,7 @@ const { ContentCreatorApplication, } = require("../models/ContentCreatorApplication"); const requireLogin = require("../middlewares/requireLogin"); +const { UserModel } = require("../models/User"); // Content Creator Application Route router.post("/course/", async (req, res) => { @@ -76,11 +77,12 @@ router.post("/course/update", requireLogin, async (req, res) => { res.send("Course Update Complete"); }); -// Get all courses for user +/** Get all courses for user router.get("/course/getall", async (req, res) => { const list = await CourseModel.find({ _user: req.user.id }); res.send(list); }); +**/ // Get all courses for user router.get("/course/eml/getall", async (req, res) => { @@ -322,7 +324,7 @@ router.post("/section/delete", requireLogin, async (req, res) => { res.send(sectionIds); }); -//Create Component +// Create Component router.post("/component/create", async (req, res) => { const { type, section_id } = req.body; // Or query?... @@ -437,4 +439,51 @@ router.get("/course/delete_all", requireLogin, async (req, res) => { res.send("Completed"); }); +// User route +router.post("/user/", async (req, res) => { + const { googleID } = req.body; + + const user = new UserModel({ + googleID: googleID, + email: email, + password: password, + joinedAt: Date.now(), + modifiedAt: Date.now(), + subscriptions: [] + }); + + try { + await user.save(); + res.send(user); + } catch (err) { + res.status(422).send(err); + } +}); + +// Subscribe to course + +router.post("/course/subscribe", async (req, res) => { + const {subscriptions } = req.body; + + course = await CourseModel.findById(course_id); + + list = UserModel.findOne({ _id: subscriptions}); + + list.push(course); + +}); + +// Get users subscriptions + +router.get("/user/subscription/getAll", async (req, res) => { + const { subscriptions } = req.body; + let list = []; + for (let i = 0; i < subscriptions.length; i++) { + const temp = await UserModel.findOne({ _id: subscriptions[i] }); + list.push(temp); + } + res.send(list); +}); + + module.exports = router; From 3a390370c0d71e76064fbd9051ac13ada41042d6 Mon Sep 17 00:00:00 2001 From: Profft Date: Fri, 22 Sep 2023 11:34:22 +0200 Subject: [PATCH 005/129] .gitignore update --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5e4f90a2..a2b309e8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ package-lock.json dev.js config/.env config/gcp_service.json +*.idea \ No newline at end of file From 65851b6e77b0c90e37ac88204b99eb0ecfac4877 Mon Sep 17 00:00:00 2001 From: PrometheusBork Date: Fri, 22 Sep 2023 15:44:14 +0200 Subject: [PATCH 006/129] Added dummy course route --- models/Courses.js | 2 +- routes/courseRoutes.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/models/Courses.js b/models/Courses.js index 8979dbab..fd1e9496 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -18,6 +18,6 @@ const courseSchema = new Schema({ sections: [{ type: Schema.Types.ObjectId, ref: "Component" }], }); -const CourseModel = mongoose.model("courses", courseSchema); // Create new collection called courses, using the courseScema +const CourseModel = mongoose.model("courses", courseSchema); // Create new collection called courses, using the courseSchema module.exports = { CourseModel } diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 883f692d..7ba05334 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -54,6 +54,16 @@ router.post("/courses", async (req, res) => { } }); +router.get("/course/getHome", async (req, res) => { + + res.send(getHome(req, res)); +}); + +function getHome(req, res){ + return "hello course"; +} + + // Update Course router.post("/course/update", requireLogin, async (req, res) => { const { course } = req.body; From d339a4be0f59bc52f281f162e3ecce0605f0e76b Mon Sep 17 00:00:00 2001 From: Profft Date: Fri, 22 Sep 2023 15:46:58 +0200 Subject: [PATCH 007/129] Test route --- routes/courseRoutes.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 883f692d..7ba05334 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -54,6 +54,16 @@ router.post("/courses", async (req, res) => { } }); +router.get("/course/getHome", async (req, res) => { + + res.send(getHome(req, res)); +}); + +function getHome(req, res){ + return "hello course"; +} + + // Update Course router.post("/course/update", requireLogin, async (req, res) => { const { course } = req.body; From c689c90d1ab8b593a6b7c9e8adaf8d2233199d87 Mon Sep 17 00:00:00 2001 From: Profft Date: Sat, 23 Sep 2023 20:44:36 +0200 Subject: [PATCH 008/129] Made route to get all subscribed courses of logged in user --- routes/courseRoutes.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 7ba05334..ff29104b 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -4,6 +4,7 @@ const router = require("express").Router(); const { CourseModel } = require("../models/Courses"); const { SectionModel } = require("../models/Sections"); const { ComponentModel } = require("../models/Components"); +const { UserModel } = require("../models/User"); const { ContentCreatorApplication, } = require("../models/ContentCreatorApplication"); @@ -56,12 +57,16 @@ router.post("/courses", async (req, res) => { router.get("/course/getHome", async (req, res) => { - res.send(getHome(req, res)); + res.send("Hello course"); }); -function getHome(req, res){ - return "hello course"; -} +router.get("/course/getSubscribedCourses", async (req, res) => { + const subscribedCourses = JSON.parse(JSON.stringify(await UserModel.findById('650c26466fe6094f6214a4a4', 'subscriptions -_id'))).subscriptions; + //const subscribedCourses = JSON.parse(JSON.stringify(await UserModel.findById(req.user.id, 'subscriptions -_id'))).subscriptions; + const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); + console.log(list) + res.send(list); +}); // Update Course From 107a1202cae7d8217deb604eec83e153e79caefb Mon Sep 17 00:00:00 2001 From: Profft Date: Sat, 23 Sep 2023 20:47:44 +0200 Subject: [PATCH 009/129] Added a comment to the route --- routes/courseRoutes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index ff29104b..c33f63b2 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -60,6 +60,7 @@ router.get("/course/getHome", async (req, res) => { res.send("Hello course"); }); +//Route that fetched all subscribed courses of logged in user. It is using a fixed user now, but the out commented function is for the logged in user router.get("/course/getSubscribedCourses", async (req, res) => { const subscribedCourses = JSON.parse(JSON.stringify(await UserModel.findById('650c26466fe6094f6214a4a4', 'subscriptions -_id'))).subscriptions; //const subscribedCourses = JSON.parse(JSON.stringify(await UserModel.findById(req.user.id, 'subscriptions -_id'))).subscriptions; From 7b15adf0455c8210e67c94b5b46605f6accb577b Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 25 Sep 2023 09:12:10 +0200 Subject: [PATCH 010/129] route fix --- routes/courseRoutes.js | 14 +++----- routes/mailRoutes.js | 2 +- routes/signupRoutes.js | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 43b4ad47..d2990e93 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -9,6 +9,7 @@ const { } = require("../models/ContentCreatorApplication"); const requireLogin = require("../middlewares/requireLogin"); const { UserModel } = require("../models/User"); +const { IdentityStore } = require("aws-sdk"); // Content Creator Application Route router.post("/course/", async (req, res) => { @@ -462,19 +463,14 @@ router.post("/user/", async (req, res) => { // Subscribe to course -router.post("/course/subscribe", async (req, res) => { - const {subscriptions } = req.body; - - course = await CourseModel.findById(course_id); - - list = UserModel.findOne({ _id: subscriptions}); - - list.push(course); +router.post("/course/:id/subscribe", async (req, res) => { + //const {user_id, id} = req.body; + + UserModel.updateOne({_id: "650d899d3bbf599a6125fdff", $push: { subscriptions: "650c26466fe6094f6214a4a4" }}, ) }); // Get users subscriptions - router.get("/user/subscription/getAll", async (req, res) => { const { subscriptions } = req.body; let list = []; diff --git a/routes/mailRoutes.js b/routes/mailRoutes.js index 4cc91ff6..1ed9785a 100644 --- a/routes/mailRoutes.js +++ b/routes/mailRoutes.js @@ -1,5 +1,5 @@ const router = require("express").Router(); -const mail = require("../helpers/email"); +const mail = require("../helpers/Email"); //send an email router.get("/send_mail", async (req, res) => { diff --git a/routes/signupRoutes.js b/routes/signupRoutes.js index 8da37aeb..2c5860ad 100644 --- a/routes/signupRoutes.js +++ b/routes/signupRoutes.js @@ -18,4 +18,78 @@ router.post("/content-creator", async (req, res) => { } }); + +router.post("/content-creator/delete", async (req, res) => { + const {account_id } = req.body + + + +}); + +router.post("/user", async (req, res) => { + const form = req.body; + form.joinedAt = Date.now(); + form.modifiedAt = Date.now(); + + // Validate form ... + if(isMissing(form.password)){ + res.status(400); + res.send("Error 400: Password is required"); + return; + } + + try { + //validateEmail(form.email); + //validateName(form.name); + const doc = UserModel(form); + const created = await doc.save(); + + res.status(201); + res.send(created); + } catch (error) { + res.status(400); + res.send("Error: " + error.message); + } +}); + +module.exports = router; + +function validateEmail(input) { + if (isMissing(input)) { + throw new Error("Email is required"); + } + if (input.length < 3) { + throw new Error("Email must be at least 3 characters"); + } + if (!input.includes("@") || !input.includes(".")) { + throw new Error("Email must contain '@' and '.'"); + } + if (!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(input))) { + throw new Error("Invalid email") + } + + return true; +} + +function validateName(input) { + if (isMissing(input)) { + throw new Error("Name is required"); + } + if (input.length < 2 || input.length > 50) { + throw new Error("Name must be between 2 and 50 characters"); + } + if(!(input.match(/^(\p{L}+ )*\p{L}+$/u))){ + throw new Error("Name must contain only letters (seperate names with spaces)"); + } + + return true; +} + + +function isMissing(input) { + return input === undefined || input === null || input === ""; +} + + + module.exports = router; From 26e3acd25b223e59be545e0bad6fde141803c8e3 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 25 Sep 2023 11:12:48 +0200 Subject: [PATCH 011/129] work --- models/Courses.js | 1 - models/User.js | 2 +- routes/courseRoutes.js | 30 ++++++++++++++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/models/Courses.js b/models/Courses.js index 8979dbab..05ab7244 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -9,7 +9,6 @@ const { Schema } = mongoose; const courseSchema = new Schema({ title: String, description: String, - _user: { type: Schema.Types.ObjectId, ref: "User" }, dateCreated: Date, dateUpdated: Date, coverImg: String, diff --git a/models/User.js b/models/User.js index 46bb51ba..7f8f4d69 100644 --- a/models/User.js +++ b/models/User.js @@ -9,7 +9,7 @@ const userSchema = new Schema({ password: String, joinedAt: Date, modifiedAt: Date, - _subscriptions: [{ type: Schema.Types.ObjectId, ref: "Course" }] + subscriptions: [String] }); const UserModel = mongoose.model("users", userSchema); diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index d2990e93..40dfdb91 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -1,5 +1,10 @@ const router = require("express").Router(); +const express = require('express'); +const app = express(); +app.use(express.json()); +app.use(express.urlencoded({extended: true})); + // Models const { CourseModel } = require("../models/Courses"); const { SectionModel } = require("../models/Sections"); @@ -464,21 +469,30 @@ router.post("/user/", async (req, res) => { // Subscribe to course router.post("/course/:id/subscribe", async (req, res) => { - //const {user_id, id} = req.body; + const {user_id, course_id} = req.body; - UserModel.updateOne({_id: "650d899d3bbf599a6125fdff", $push: { subscriptions: "650c26466fe6094f6214a4a4" }}, ) + UserModel.findOneAndUpdate( + { _id: user_id}, + { $push: { subscribtions: course_id} +}); + + user = await UserModel.findById(user_id); + res.send(user) }); // Get users subscriptions router.get("/user/subscription/getAll", async (req, res) => { - const { subscriptions } = req.body; - let list = []; - for (let i = 0; i < subscriptions.length; i++) { - const temp = await UserModel.findOne({ _id: subscriptions[i] }); - list.push(temp); + const {user_id } = req.body + + const subscribtions = UserModel.find({_id: user_id}).subscriptions; + + let subscription_list = []; + for (let i = 0; i < subscribtions.length; i++) { + const course = await UserModel.find({_id: user_id}).subscriptions[i]; + list.push(course); } - res.send(list); + res.send(subscription_list); }); From 1d1d51fdee321bbb799c19a8d177f03a6a75175c Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 25 Sep 2023 13:11:22 +0200 Subject: [PATCH 012/129] fix --- models/User.js | 2 +- routes/courseRoutes.js | 37 ++++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/models/User.js b/models/User.js index 7f8f4d69..97b53633 100644 --- a/models/User.js +++ b/models/User.js @@ -9,7 +9,7 @@ const userSchema = new Schema({ password: String, joinedAt: Date, modifiedAt: Date, - subscriptions: [String] + subscriptions: [{ type: Schema.Types.ObjectId, ref: "Course" }] }); const UserModel = mongoose.model("users", userSchema); diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 40dfdb91..5af650d2 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -466,30 +466,45 @@ router.post("/user/", async (req, res) => { } }); -// Subscribe to course +// Subscribe to course TODO: check for duplicates router.post("/course/:id/subscribe", async (req, res) => { - const {user_id, course_id} = req.body; - - UserModel.findOneAndUpdate( - { _id: user_id}, - { $push: { subscribtions: course_id} + const { user_id, course_id} = req.body; + + (await UserModel.findOneAndUpdate( + { _id: user_id }, + { $push: { subscriptions: course_id} })) + .save; + + user = await UserModel.findById(user_id); + res.send(user) + }); +router.post("/user/subscriptions/unsubscribe", async (req, res) => { + const { user_id, course_id} = req.body; + + (await UserModel.findOneAndUpdate( + { _id: user_id }, + { $pull: { subscriptions: course_id} })) + .save; + user = await UserModel.findById(user_id); res.send(user) }); // Get users subscriptions -router.get("/user/subscription/getAll", async (req, res) => { - const {user_id } = req.body +router.get("/user/subscriptions/getAll", async (req, res) => { + const {user_id} = req.body - const subscribtions = UserModel.find({_id: user_id}).subscriptions; + const sub = UserModel.findOne({ _id: user_id }, + { subscriptions: course_id} ); let subscription_list = []; - for (let i = 0; i < subscribtions.length; i++) { - const course = await UserModel.find({_id: user_id}).subscriptions[i]; + + for (let i = 0; i < sub.length; i++) { + const course = await user.subscriptions[i]; list.push(course); } res.send(subscription_list); From bc0281de62d0ccfaf92c35f858ccafdda425e8c3 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 25 Sep 2023 13:22:09 +0200 Subject: [PATCH 013/129] Subscribe fix --- routes/courseRoutes.js | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index c33f63b2..e8bcda33 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -453,4 +453,64 @@ router.get("/course/delete_all", requireLogin, async (req, res) => { res.send("Completed"); }); +// User route +router.post("/user/", async (req, res) => { + const { googleID } = req.body; + + const user = new UserModel({ + googleID: googleID, + email: email, + password: password, + joinedAt: Date.now(), + modifiedAt: Date.now(), + subscriptions: [] + }); + + try { + await user.save(); + res.send(user); + } catch (err) { + res.status(422).send(err); + } +}); + +// Subscribe to course TODO: check for duplicates + +router.post("/course/:id/subscribe", async (req, res) => { + const { user_id, course_id} = req.body; + + (await UserModel.findOneAndUpdate( + { _id: user_id }, + { $push: { subscriptions: course_id} })) + .save; + + user = await UserModel.findById(user_id); + res.send(user) + +}); + +router.post("/user/subscriptions/unsubscribe", async (req, res) => { + const { user_id, course_id} = req.body; + + (await UserModel.findOneAndUpdate( + { _id: user_id }, + { $pull: { subscriptions: course_id} })) + .save; + + user = await UserModel.findById(user_id); + res.send(user) + +}); + +// Get users subscriptions +router.get("/user/subscriptions/getAll", async (req, res) => { + const {user_id} = req.body + const subscribedCourses = (await UserModel.findById(user_id, 'subscriptions -_id')).subscriptions; + const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); + + console.log(list) + res.send(list); +}); + + module.exports = router; From 585857614b64991d56908e56154aabf12046b43d Mon Sep 17 00:00:00 2001 From: sofiiagran <92582321+sofiiagran@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:25:30 +0200 Subject: [PATCH 014/129] Update User.js model --- models/User.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/User.js b/models/User.js index 7a8e7a17..97b53633 100644 --- a/models/User.js +++ b/models/User.js @@ -8,7 +8,8 @@ const userSchema = new Schema({ email: String, password: String, joinedAt: Date, - modifiedAt: Date + modifiedAt: Date, + subscriptions: [{ type: Schema.Types.ObjectId, ref: "Course" }] }); const UserModel = mongoose.model("users", userSchema); From 5e6c11973e2463d642085e7144c5e0af350e5020 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 25 Sep 2023 13:38:38 +0200 Subject: [PATCH 015/129] course fix Course now refers to a creator instead of a user --- models/Courses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Courses.js b/models/Courses.js index fd1e9496..31e66a6f 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -9,7 +9,7 @@ const { Schema } = mongoose; const courseSchema = new Schema({ title: String, description: String, - _user: { type: Schema.Types.ObjectId, ref: "User" }, + _creator: { type: Schema.Types.ObjectId, ref: "Creator" }, dateCreated: Date, dateUpdated: Date, coverImg: String, From fd9e3232701bd9e38ecc75f0ff47b7a5b34ba8a3 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 26 Sep 2023 10:56:08 +0200 Subject: [PATCH 016/129] course fix --- models/Courses.js | 1 + 1 file changed, 1 insertion(+) diff --git a/models/Courses.js b/models/Courses.js index 05ab7244..b0dcb1f7 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -15,6 +15,7 @@ const courseSchema = new Schema({ category: String, published: Boolean, sections: [{ type: Schema.Types.ObjectId, ref: "Component" }], + creator: [{ type: Schema.Types.ObjectId, ref: "ContentCreator" }] }); const CourseModel = mongoose.model("courses", courseSchema); // Create new collection called courses, using the courseScema From bee52f99082ae0ff23f3f6eb511d704b35c10f4e Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 26 Sep 2023 11:01:19 +0200 Subject: [PATCH 017/129] course fix --- models/Courses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Courses.js b/models/Courses.js index 31e66a6f..03a9a257 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -9,7 +9,7 @@ const { Schema } = mongoose; const courseSchema = new Schema({ title: String, description: String, - _creator: { type: Schema.Types.ObjectId, ref: "Creator" }, + creator: { type: Schema.Types.ObjectId, ref: "Creator" }, dateCreated: Date, dateUpdated: Date, coverImg: String, From 5618b64d1ae4e61d13203751193e7a34a9abeea7 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 26 Sep 2023 11:09:23 +0200 Subject: [PATCH 018/129] fix --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a2b309e8..6891663f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ package-lock.json dev.js config/.env config/gcp_service.json -*.idea \ No newline at end of file +*.idea From 3694f9ad22b4a21a34c98f367c155f7e331afe42 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 26 Sep 2023 11:40:57 +0200 Subject: [PATCH 019/129] Rename UserModel changed to User --- routes/courseRoutes.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index e8bcda33..0658dc34 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -4,7 +4,7 @@ const router = require("express").Router(); const { CourseModel } = require("../models/Courses"); const { SectionModel } = require("../models/Sections"); const { ComponentModel } = require("../models/Components"); -const { UserModel } = require("../models/User"); +const { User } = require("../models/User"); const { ContentCreatorApplication, } = require("../models/ContentCreatorApplication"); @@ -479,12 +479,12 @@ router.post("/user/", async (req, res) => { router.post("/course/:id/subscribe", async (req, res) => { const { user_id, course_id} = req.body; - (await UserModel.findOneAndUpdate( + (await User.findOneAndUpdate( { _id: user_id }, { $push: { subscriptions: course_id} })) .save; - user = await UserModel.findById(user_id); + user = await User.findById(user_id); res.send(user) }); @@ -492,12 +492,12 @@ router.post("/course/:id/subscribe", async (req, res) => { router.post("/user/subscriptions/unsubscribe", async (req, res) => { const { user_id, course_id} = req.body; - (await UserModel.findOneAndUpdate( + (await User.findOneAndUpdate( { _id: user_id }, { $pull: { subscriptions: course_id} })) .save; - user = await UserModel.findById(user_id); + user = await User.findById(user_id); res.send(user) }); @@ -505,10 +505,9 @@ router.post("/user/subscriptions/unsubscribe", async (req, res) => { // Get users subscriptions router.get("/user/subscriptions/getAll", async (req, res) => { const {user_id} = req.body - const subscribedCourses = (await UserModel.findById(user_id, 'subscriptions -_id')).subscriptions; + const subscribedCourses = (await User.findById(user_id, 'subscriptions -_id')).subscriptions; const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); - console.log(list) res.send(list); }); From 4612d7fd09869c1f5dae0682293634022008c25a Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 27 Sep 2023 12:23:14 +0200 Subject: [PATCH 020/129] courseRoute fix --- routes/courseRoutes.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 0658dc34..0cf2fdc6 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -474,9 +474,10 @@ router.post("/user/", async (req, res) => { } }); -// Subscribe to course TODO: check for duplicates +// Subscribe to course +// TODO: check for duplicates -router.post("/course/:id/subscribe", async (req, res) => { +router.post("/course/subscribe", async (req, res) => { const { user_id, course_id} = req.body; (await User.findOneAndUpdate( @@ -484,12 +485,13 @@ router.post("/course/:id/subscribe", async (req, res) => { { $push: { subscriptions: course_id} })) .save; - user = await User.findById(user_id); + let user = await User.findById(user_id); res.send(user) }); -router.post("/user/subscriptions/unsubscribe", async (req, res) => { +// Unsubscribe to course +router.post("/course/unsubscribe", async (req, res) => { const { user_id, course_id} = req.body; (await User.findOneAndUpdate( @@ -497,7 +499,7 @@ router.post("/user/subscriptions/unsubscribe", async (req, res) => { { $pull: { subscriptions: course_id} })) .save; - user = await User.findById(user_id); + let user = await User.findById(user_id); res.send(user) }); From 976b85166822d2daa35405ed2910378d1993d216 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 27 Sep 2023 13:51:00 +0200 Subject: [PATCH 021/129] route fix --- models/ContentCreatorApplication.js | 1 + routes/contentCreatorRoutes.js | 30 +++++++++++++++++++++++++++++ routes/courseRoutes.js | 19 ++---------------- 3 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 routes/contentCreatorRoutes.js diff --git a/models/ContentCreatorApplication.js b/models/ContentCreatorApplication.js index 684b766a..cfd0d90b 100644 --- a/models/ContentCreatorApplication.js +++ b/models/ContentCreatorApplication.js @@ -19,4 +19,5 @@ const ContentCreatorApplication = mongoose.model( ContentCreatorSchema ); + module.exports = { ContentCreatorApplication }; diff --git a/routes/contentCreatorRoutes.js b/routes/contentCreatorRoutes.js new file mode 100644 index 00000000..6574f402 --- /dev/null +++ b/routes/contentCreatorRoutes.js @@ -0,0 +1,30 @@ +const router = require("express").Router(); + +const express = require('express'); +const app = express(); +app.use(express.json()); +app.use(express.urlencoded({extended: true})); + +// Models + +const { + ContentCreatorApplication, +} = require("../models/ContentCreatorApplication"); + +router.delete("/content-creator/delete/:id", async (req, res) => { + try { + // Get the authenticated creator's ID from req.creator.id + const { id } = req.params; + + // Use Mongoose to find and delete the user by ID + console.log("Deleting creator with ID:", id) + await User.findByIdAndDelete(id); + + // Send a success response + res.status(200).json({ message: "Content creator deleted successfully" }); + } catch (error) { + // Handle any errors and send an error response + console.error("Error deleting content creator:", error); + res.status(500).json({ error: "An error occurred while deleting the content creator" }); + } + }); diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 8cbb1ba1..0823c385 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -62,21 +62,6 @@ router.post("/courses", async (req, res) => { } }); -router.get("/course/getHome", async (req, res) => { - - res.send("Hello course"); -}); - -//Route that fetched all subscribed courses of logged in user. It is using a fixed user now, but the out commented function is for the logged in user -router.get("/course/getSubscribedCourses", async (req, res) => { - const subscribedCourses = JSON.parse(JSON.stringify(await UserModel.findById('650c26466fe6094f6214a4a4', 'subscriptions -_id'))).subscriptions; - //const subscribedCourses = JSON.parse(JSON.stringify(await UserModel.findById(req.user.id, 'subscriptions -_id'))).subscriptions; - const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); - console.log(list) - res.send(list); -}); - - // Update Course router.post("/course/update", requireLogin, async (req, res) => { const { course } = req.body; @@ -244,7 +229,7 @@ router.post("/section/create", requireLogin, async (req, res) => { }); // Get all sections -router.post("/course/getallsections", requireLogin, async (req, res) => { +router.post("/course/sections", requireLogin, async (req, res) => { const { sections } = req.body; let list = []; for (let i = 0; i < sections.length; i++) { @@ -513,7 +498,7 @@ router.post("/course/unsubscribe", async (req, res) => { }); // Get users subscriptions -router.get("/user/subscriptions/getAll", async (req, res) => { +router.get("/user/subscriptions/all", async (req, res) => { const {user_id} = req.body const subscribedCourses = (await User.findById(user_id, 'subscriptions -_id')).subscriptions; const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); From ec7451efef6264f832c609378f3b689b3cd43275 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Fri, 29 Sep 2023 14:12:02 +0200 Subject: [PATCH 022/129] routefix --- routes/contentCreatorRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/contentCreatorRoutes.js b/routes/contentCreatorRoutes.js index 6574f402..833ade90 100644 --- a/routes/contentCreatorRoutes.js +++ b/routes/contentCreatorRoutes.js @@ -11,7 +11,7 @@ const { ContentCreatorApplication, } = require("../models/ContentCreatorApplication"); -router.delete("/content-creator/delete/:id", async (req, res) => { +router.delete("/profile/delete/:id", async (req, res) => { try { // Get the authenticated creator's ID from req.creator.id const { id } = req.params; From 59cdeb5b5fb5dfaf3d2a21b1bb1ffd0bac2a406c Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 2 Oct 2023 18:05:48 +0200 Subject: [PATCH 023/129] fix login --- routes/authRoutes.js | 3 ++- routes/courseRoutes.js | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 7a4f56dc..e89261d3 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -8,7 +8,7 @@ const keys = require("../config/keys"); // Import keys from config/keys.js const { makeExpressCallback } = require('../helpers/express') const { authEndpointHandler } = require('../auth'); const { signAccessToken } = require('../helpers/token'); -const { compare } = require('../helpers/Password'); +const { compare } = require('../helpers/password'); // Services require("../services/passport"); @@ -61,6 +61,7 @@ router.post("/auth/login", async (req, res) => { user: { name: user.name, email: user.email, + id: user.id, }, }); } else { diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 0823c385..33053654 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -92,7 +92,7 @@ router.get("/course/getall", async (req, res) => { **/ // Get all courses for user -router.get("/course/eml/getall", async (req, res) => { +router.get("/course/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); @@ -468,7 +468,6 @@ router.post("/user/", async (req, res) => { }); // Subscribe to course -// TODO: check for duplicates router.post("/course/subscribe", async (req, res) => { const { user_id, course_id} = req.body; From a355865c2a6f88619e6b441e719934b11573d368 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 3 Oct 2023 10:56:51 +0200 Subject: [PATCH 024/129] Update courseRoutes.js --- routes/courseRoutes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 33053654..d6ccb4b7 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -469,8 +469,9 @@ router.post("/user/", async (req, res) => { // Subscribe to course -router.post("/course/subscribe", async (req, res) => { - const { user_id, course_id} = req.body; +router.post("/course/:id/subscribe", async (req, res) => { + const { user_id } = req.body; + const { course_id } = req.params.id; (await User.findOneAndUpdate( { _id: user_id }, From b3656589718b4cb03d23925e5af7493fa9aaf56a Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 3 Oct 2023 21:16:02 +0200 Subject: [PATCH 025/129] Update courseRoutes.js --- routes/courseRoutes.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index d6ccb4b7..8fa10104 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -92,7 +92,7 @@ router.get("/course/getall", async (req, res) => { **/ // Get all courses for user -router.get("/course/all", async (req, res) => { +router.get("/courses/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); @@ -469,9 +469,8 @@ router.post("/user/", async (req, res) => { // Subscribe to course -router.post("/course/:id/subscribe", async (req, res) => { - const { user_id } = req.body; - const { course_id } = req.params.id; +router.post("/course/subscribe", async (req, res) => { + const { user_id, course_id } = req.body; (await User.findOneAndUpdate( { _id: user_id }, @@ -507,4 +506,17 @@ router.get("/user/subscriptions/all", async (req, res) => { }); +// Check if in database +async function checkIfSubscribed(userId, courseId) { + const subscribedCourses = (await User.findById(userId, 'subscriptions -_id')).subscriptions; + const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); + + if(list.include(courseId)) { + return true; + } else { + return false; + } +} + + module.exports = router; From e29e5f2b793fa7caacb3c4bb42d394bbb11b8f5e Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 4 Oct 2023 09:08:56 +0200 Subject: [PATCH 026/129] Update courseRoutes.js --- routes/courseRoutes.js | 71 +++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 8fa10104..923e619f 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -84,19 +84,43 @@ router.post("/course/update", requireLogin, async (req, res) => { res.send("Course Update Complete"); }); -/** Get all courses for user -router.get("/course/getall", async (req, res) => { - const list = await CourseModel.find({ _user: req.user.id }); - res.send(list); -}); -**/ - -// Get all courses for user +//Get all courses router.get("/courses/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); + +// Get all courses id +router.get("/courses/all/id", async (req, res) => { + + try { + // Searching for all courses in database + const course = await CourseModel.find(); + console.log("ID: " + course._id); + if (!course) { + // If no course is found, return an error message + console.log("No courses found") + return res.status(404).json({ + "message": "No courses found" + }); + } else { + console.log("ID: " + course.id); + return res.status(202).json({ + status: 'course fetched successful', + course: { + id: course.id, + }, + }); + } + } catch (err) { + console.log(err) + return res.status(500).json({ + "error": { "code": 500, "message": "Server could not be reached" } + }); + } +}); + // FIXME: no error handling, just needed the endpoint - Mvh. Frederik router.get("/course/:id", async (req, res) => { const { id } = req.params; // destructure params @@ -470,53 +494,42 @@ router.post("/user/", async (req, res) => { // Subscribe to course router.post("/course/subscribe", async (req, res) => { - const { user_id, course_id } = req.body; + const { userId, courseId } = req.body; (await User.findOneAndUpdate( - { _id: user_id }, - { $push: { subscriptions: course_id} })) + { _id: userId }, + { $push: { subscriptions: courseId} })) .save; - let user = await User.findById(user_id); + let user = await User.findById(userId); res.send(user) }); // Unsubscribe to course router.post("/course/unsubscribe", async (req, res) => { - const { user_id, course_id} = req.body; + const { userId, courseId} = req.body; (await User.findOneAndUpdate( - { _id: user_id }, - { $pull: { subscriptions: course_id} })) + { _id: userId }, + { $pull: { subscriptions: courseId} })) .save; - let user = await User.findById(user_id); + let user = await User.findById(userId); res.send(user) }); // Get users subscriptions router.get("/user/subscriptions/all", async (req, res) => { - const {user_id} = req.body - const subscribedCourses = (await User.findById(user_id, 'subscriptions -_id')).subscriptions; + const {userId} = req.body; + const subscribedCourses = (await User.findById(userId, 'subscriptions -_id')).subscriptions; const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); res.send(list); }); -// Check if in database -async function checkIfSubscribed(userId, courseId) { - const subscribedCourses = (await User.findById(userId, 'subscriptions -_id')).subscriptions; - const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); - - if(list.include(courseId)) { - return true; - } else { - return false; - } -} module.exports = router; From 0622dcb3f0e1a2048b076e0a353ced4ffacd2103 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 4 Oct 2023 11:31:50 +0200 Subject: [PATCH 027/129] Update courseRoutes.js --- routes/courseRoutes.js | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 923e619f..5b223e13 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -85,7 +85,7 @@ router.post("/course/update", requireLogin, async (req, res) => { }); //Get all courses -router.get("/courses/all", async (req, res) => { +router.get("/courses", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); @@ -494,39 +494,55 @@ router.post("/user/", async (req, res) => { // Subscribe to course router.post("/course/subscribe", async (req, res) => { - const { userId, courseId } = req.body; + const { user_id, course_id } = req.body; + (await User.findOneAndUpdate( - { _id: userId }, - { $push: { subscriptions: courseId} })) + { _id: user_id }, + { $push: { subscriptions: course_id} })) .save; - let user = await User.findById(userId); + let user = await User.findById(user_id); res.send(user) }); // Unsubscribe to course router.post("/course/unsubscribe", async (req, res) => { - const { userId, courseId} = req.body; + const { user_id, course_id} = req.body; (await User.findOneAndUpdate( - { _id: userId }, - { $pull: { subscriptions: courseId} })) + { _id: user_id }, + { $pull: { subscriptions: course_id} })) .save; - let user = await User.findById(userId); + let user = await User.findById(user_id); res.send(user) }); // Get users subscriptions router.get("/user/subscriptions/all", async (req, res) => { - const {userId} = req.body; - const subscribedCourses = (await User.findById(userId, 'subscriptions -_id')).subscriptions; - const list = await CourseModel.find({'_id': { $in: subscribedCourses }}); + try { + const { user_id } = req.query; + + // Find the user by _id and select the 'subscriptions' field + const user = await User.findById(user_id).select('subscriptions'); - res.send(list); + if (!user) { + return res.status(404).json({ message: 'User not found' }); + } + + const subscribedCourses = user.subscriptions; + + // Find courses based on the subscribed course IDs + const list = await CourseModel.find({ '_id': { $in: subscribedCourses } }); + + res.send(list); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } }); From 225f9873a781f1dffb307a50adef4ffd10439c75 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 4 Oct 2023 22:45:09 +0200 Subject: [PATCH 028/129] yesb --- models/Components.js | 18 ++++------ models/ContentCreatorApplication.js | 10 +++--- models/Courses.js | 14 +++++--- models/Exercises.js | 24 +++++++++++++ models/Sections.js | 13 ++++--- models/User.js | 2 +- routes/courseRoutes.js | 56 +++++++++++++++++++++++------ 7 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 models/Exercises.js diff --git a/models/Components.js b/models/Components.js index e1a5f126..d48cea05 100644 --- a/models/Components.js +++ b/models/Components.js @@ -1,16 +1,12 @@ -// Mongoose model class for Courses const mongoose = require("mongoose"); const { Schema } = mongoose; -// Class description -const componentsSchema = new Schema({ - type: String, // Image / Video / Audio / Text - file: String, // AWS URL, if video, audio or image - text: String, // IF component is text - dateCreated: Date, // For all components - dateUpdated: Date, // If its a text component +const component = new Schema({ + _id: Schema.Types.ObjectId, + fileName: String, + path: String, + size: Number, + type: String, }); -const ComponentModel = mongoose.model("components", componentsSchema); - -module.exports = { ComponentModel } +module.exports = { component }; diff --git a/models/ContentCreatorApplication.js b/models/ContentCreatorApplication.js index cfd0d90b..f96984cb 100644 --- a/models/ContentCreatorApplication.js +++ b/models/ContentCreatorApplication.js @@ -3,7 +3,7 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; // Class description -const ContentCreatorSchema = new Schema({ +const contentCreatorSchema = new Schema({ firstName: { type: String, required: true }, lastName: { type: String, required: true }, email: { type: String, required: true }, @@ -14,10 +14,10 @@ const ContentCreatorSchema = new Schema({ modifiedAt: { type: Date }, }); -const ContentCreatorApplication = mongoose.model( - "Content-Creator-Application", - ContentCreatorSchema +const ContentCreator = mongoose.model( + "contentCreator", + contentCreatorSchema ); -module.exports = { ContentCreatorApplication }; +module.exports = { ContentCreator }; diff --git a/models/Courses.js b/models/Courses.js index be85f33e..a44af5ac 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -1,6 +1,7 @@ // Mongoose model class for Courses const mongoose = require("mongoose"); const { Schema } = mongoose; +const { component } = require("./Components"); // Routes are sorted into COURSE - SECTION - COMPONENT each with ASCII art, within each functions are in order of CRUD // NOTE Files do NOT delete from the backend yet, on the TODO as of 03/2022 @@ -11,13 +12,16 @@ const courseSchema = new Schema({ description: String, dateCreated: Date, dateUpdated: Date, - coverImg: String, + coverImg: component, category: String, published: Boolean, - sections: [{ type: Schema.Types.ObjectId, ref: "Component" }], - creator: [{ type: Schema.Types.ObjectId, ref: "Creator" }] + sections: [{ type: Schema.Types.ObjectId, ref: "sections" }], + creator: [{ type: Schema.Types.ObjectId, ref: "contentCreator" }] }); -const CourseModel = mongoose.model("courses", courseSchema); // Create new collection called courses, using the courseSchema +const CourseModel = mongoose.model( + "course", + courseSchema +); -module.exports = { CourseModel } +module.exports = { CourseModel } \ No newline at end of file diff --git a/models/Exercises.js b/models/Exercises.js new file mode 100644 index 00000000..0494b9b7 --- /dev/null +++ b/models/Exercises.js @@ -0,0 +1,24 @@ +const mongoose = require("mongoose") +const { component } = require("./Components"); +const { Schema } = mongoose; + +const exerciseSchema = new Schema ({ + parentSection: Schema.Types.ObjectId, + title: String, + description: String, + content: component, + answers: { + text: String, + correct: Boolean, + modifiedAt: Date + }, + onWrongFeedback: component, + modifiedAt: Date, +}) + +const ExerciseModel = mongoose.model( + "exercise", + exerciseSchema + ); + + module.exports = { ExerciseModel } \ No newline at end of file diff --git a/models/Sections.js b/models/Sections.js index 0876354a..f193549e 100644 --- a/models/Sections.js +++ b/models/Sections.js @@ -2,13 +2,18 @@ const mongoose = require("mongoose"); const { Schema } = mongoose; -// Class description +// Class description + const sectionSchema = new Schema({ + exercises: [{ type: Schema.Types.ObjectId, ref: "exercise" }], title: String, description: String, - dateCreated: Date, - dateUpdated: Date, - components: [{ type: Schema.Types.ObjectId, ref: "Component" }], + sectionNumber: Number, + createdAt: Date, + modifiedAt: Date, + totalPoints: Number, + components: [{ type: Schema.Types.ObjectId, ref: "component" }], + parentCourse: { type: Schema.Types.ObjectId, ref: "Course" }, }); const SectionModel = mongoose.model("sections", sectionSchema); diff --git a/models/User.js b/models/User.js index 700f9b0d..813ea3a8 100644 --- a/models/User.js +++ b/models/User.js @@ -55,7 +55,7 @@ const userSchema = new Schema({ password: String, joinedAt: Date, modifiedAt: Date, - subscriptions: [{ type: Schema.Types.ObjectId, ref: "Course" }] + subscriptions: [{ type: Schema.Types.ObjectId, ref: "course" }] }); const UserModel = mongoose.model("users", userSchema); diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 5b223e13..5fdd7478 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -446,16 +446,6 @@ router.post("/component/delete", requireLogin, async (req, res) => { res.send(componentIds); }); -router.post("/eml/course/getallsections", async (req, res) => { - const { sections } = req.body; - let list = []; - for (let i = 0; i < sections.length; i++) { - const temp = await SectionModel.findOne({ _id: sections[i] }); - list.push(temp); - } - res.send(list); -}); - // Delete all documents for user router.get("/course/delete_all", requireLogin, async (req, res) => { await CourseModel.deleteMany({ _user: req.user.id }, (err) => { @@ -491,6 +481,9 @@ router.post("/user/", async (req, res) => { } }); + + + // Subscribe to course router.post("/course/subscribe", async (req, res) => { @@ -545,6 +538,49 @@ router.get("/user/subscriptions/all", async (req, res) => { } }); +/** New (Louise) */ + +router.get("/course/:id/sections/all", async (req, res) => { + const { id } = req.params; // destructure params + const sections = await SectionModel.find({ parentCourse: id} ); + + res.send(sections); +}); + +router.get("/course/:courseId/section/:sectionId", async (req, res) => { + const { courseId, sectionId } = req.params; // destructure params + + // Find a specific section within the given course by both IDs + const section = await SectionModel.findOne({ parentCourse: courseId, _id: sectionId }); + res.send(section); +}); + +router.get("/course/:courseId/section/:sectionId/exercises/all", async (req, res) => { + const { courseId, sectionId } = req.params; // destructure params + + // Find a specific section within the given course by both IDs + const exercises = await ExerciseModel.find({ parentSection: sectionId }); + res.send(exercises); +}); + +router.get('/user', async (req, res) => { + + try { + + const { course_id, user_id } = req.query; + const user = await User.findOne({ _id: user_id, subscriptions: course_id }); + + if(user == null) { + res.send("false"); + } else { + res.send("true"); + } + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } +}); From 6e3cfd28d039ece72ab3eca965d1e11378b448ac Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 5 Oct 2023 20:29:08 +0200 Subject: [PATCH 029/129] fix --- routes/contentCreatorRoutes.js | 3 ++- routes/courseRoutes.js | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/routes/contentCreatorRoutes.js b/routes/contentCreatorRoutes.js index 833ade90..e6471dc9 100644 --- a/routes/contentCreatorRoutes.js +++ b/routes/contentCreatorRoutes.js @@ -6,7 +6,7 @@ app.use(express.json()); app.use(express.urlencoded({extended: true})); // Models - +/* const { ContentCreatorApplication, } = require("../models/ContentCreatorApplication"); @@ -28,3 +28,4 @@ router.delete("/profile/delete/:id", async (req, res) => { res.status(500).json({ error: "An error occurred while deleting the content creator" }); } }); +*/ \ No newline at end of file diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 5fdd7478..2531631e 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -17,6 +17,7 @@ const requireLogin = require("../middlewares/requireLogin"); const { UserModel } = require("../models/User"); const { IdentityStore } = require("aws-sdk"); +/* // Content Creator Application Route router.post("/course/", async (req, res) => { const { title, description } = req.body; @@ -84,11 +85,7 @@ router.post("/course/update", requireLogin, async (req, res) => { res.send("Course Update Complete"); }); -//Get all courses -router.get("/courses", async (req, res) => { - const list = await CourseModel.find(); - res.send(list); -}); + // Get all courses id @@ -460,6 +457,8 @@ router.get("/course/delete_all", requireLogin, async (req, res) => { res.send("Completed"); }); +*/ + // User route router.post("/user/", async (req, res) => { const { googleID } = req.body; @@ -482,7 +481,11 @@ router.post("/user/", async (req, res) => { }); - +//Get all courses +router.get("/course/all", async (req, res) => { + const list = await CourseModel.find(); + res.send(list); +}); // Subscribe to course @@ -538,8 +541,8 @@ router.get("/user/subscriptions/all", async (req, res) => { } }); -/** New (Louise) */ +// get all sections from course router.get("/course/:id/sections/all", async (req, res) => { const { id } = req.params; // destructure params const sections = await SectionModel.find({ parentCourse: id} ); @@ -547,6 +550,7 @@ router.get("/course/:id/sections/all", async (req, res) => { res.send(sections); }); +// get specififc course router.get("/course/:courseId/section/:sectionId", async (req, res) => { const { courseId, sectionId } = req.params; // destructure params @@ -555,6 +559,7 @@ router.get("/course/:courseId/section/:sectionId", async (req, res) => { res.send(section); }); +// get all excercies from a section router.get("/course/:courseId/section/:sectionId/exercises/all", async (req, res) => { const { courseId, sectionId } = req.params; // destructure params @@ -563,6 +568,7 @@ router.get("/course/:courseId/section/:sectionId/exercises/all", async (req, res res.send(exercises); }); +// checks if user is subscribed to a specific course router.get('/user', async (req, res) => { try { From 8af3d23076bc15e6e622d3af0f7ea9f62feab6cf Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 5 Oct 2023 21:02:09 +0200 Subject: [PATCH 030/129] some fix --- routes/courseRoutes.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 2531631e..8c289619 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -118,12 +118,6 @@ router.get("/courses/all/id", async (req, res) => { } }); -// FIXME: no error handling, just needed the endpoint - Mvh. Frederik -router.get("/course/:id", async (req, res) => { - const { id } = req.params; // destructure params - const course = await CourseModel.findById(id); - res.send(course); -}) // Update course title router.post("/course/update/title", async (req, res) => { @@ -480,9 +474,17 @@ router.post("/user/", async (req, res) => { } }); +// Get specific course +router.get("/course/:id", async (req, res) => { + const { id } = req.params; // destructure params + const course = await CourseModel.findById(id); + res.send(course); +}) + + //Get all courses -router.get("/course/all", async (req, res) => { +router.get("/courses/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); @@ -518,7 +520,7 @@ router.post("/course/unsubscribe", async (req, res) => { }); // Get users subscriptions -router.get("/user/subscriptions/all", async (req, res) => { +router.get("/user/subscription/all", async (req, res) => { try { const { user_id } = req.query; From 9a0725e555a7b0fb53eca1a939823804424726ff Mon Sep 17 00:00:00 2001 From: Profft Date: Sat, 7 Oct 2023 15:51:18 +0200 Subject: [PATCH 031/129] Change a component route to match the app call But the code used have been commented out? --- routes/courseRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 8c289619..32804998 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -370,7 +370,7 @@ router.post("/component/create", async (req, res) => { }); //Get all components -router.post("/component/getallcomponents", async (req, res) => { +router.post("/components/all", async (req, res) => { const { components } = req.body; let list = []; for (let i = 0; i < components.length; i++) { From fadfdaa5a964b96da364e4bf39f6fb911990c081 Mon Sep 17 00:00:00 2001 From: Profft Date: Sat, 7 Oct 2023 23:31:26 +0200 Subject: [PATCH 032/129] Change URL from plural to singular --- routes/courseRoutes.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 32804998..5aa4b881 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -17,6 +17,7 @@ const requireLogin = require("../middlewares/requireLogin"); const { UserModel } = require("../models/User"); const { IdentityStore } = require("aws-sdk"); +//Why is all this out commented? Have it been replaced whit something else? /* // Content Creator Application Route router.post("/course/", async (req, res) => { @@ -484,7 +485,7 @@ router.get("/course/:id", async (req, res) => { //Get all courses -router.get("/courses/all", async (req, res) => { +router.get("/course/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); From 9f3e430b6168837d33c6b55303cb884e60fc8c58 Mon Sep 17 00:00:00 2001 From: Profft Date: Sat, 7 Oct 2023 23:57:23 +0200 Subject: [PATCH 033/129] Fixed a plural to singular --- routes/courseRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 5aa4b881..52e1386a 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -371,7 +371,7 @@ router.post("/component/create", async (req, res) => { }); //Get all components -router.post("/components/all", async (req, res) => { +router.post("/component/all", async (req, res) => { const { components } = req.body; let list = []; for (let i = 0; i < components.length; i++) { From 851fe92c1caa8e6cd1058cd0155c4b2c69ffe1ef Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 9 Oct 2023 09:07:07 +0200 Subject: [PATCH 034/129] route fix --- routes/courseRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 52e1386a..1708dda4 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -485,7 +485,7 @@ router.get("/course/:id", async (req, res) => { //Get all courses -router.get("/course/all", async (req, res) => { +router.get("/courses/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); From 4514f87201bf39cf53e32e47f9732076d6f2c53b Mon Sep 17 00:00:00 2001 From: Profft Date: Mon, 9 Oct 2023 15:21:58 +0200 Subject: [PATCH 035/129] Back to plural --- routes/courseRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 52e1386a..1708dda4 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -485,7 +485,7 @@ router.get("/course/:id", async (req, res) => { //Get all courses -router.get("/course/all", async (req, res) => { +router.get("/courses/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); From 3c8849d7e058016fad7ecb831bb98d2f149a5240 Mon Sep 17 00:00:00 2001 From: PrometheusBork Date: Mon, 9 Oct 2023 15:32:27 +0200 Subject: [PATCH 036/129] Update courseRoutes.js --- routes/courseRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 52e1386a..1708dda4 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -485,7 +485,7 @@ router.get("/course/:id", async (req, res) => { //Get all courses -router.get("/course/all", async (req, res) => { +router.get("/courses/all", async (req, res) => { const list = await CourseModel.find(); res.send(list); }); From 8ac002f12210bc11902532cf47e78ee10ac42fff Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Mon, 9 Oct 2023 20:10:29 +0200 Subject: [PATCH 037/129] route fix --- routes/courseRoutes.js | 179 ++++++++++++++++++++++++++++------------- 1 file changed, 124 insertions(+), 55 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 1708dda4..4a0f513f 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -452,8 +452,6 @@ router.get("/course/delete_all", requireLogin, async (req, res) => { res.send("Completed"); }); -*/ - // User route router.post("/user/", async (req, res) => { const { googleID } = req.body; @@ -475,53 +473,148 @@ router.post("/user/", async (req, res) => { } }); -// Get specific course -router.get("/course/:id", async (req, res) => { - const { id } = req.params; // destructure params - const course = await CourseModel.findById(id); - res.send(course); -}) +*/ +/*** COURSE, SECTIONS AND EXERCISE ROUTES ***/ //Get all courses router.get("/courses/all", async (req, res) => { - const list = await CourseModel.find(); - res.send(list); + + try { + // find all courses in the database + const list = await CourseModel.find(); + res.send(list); + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + }); + +// Get specific course +router.get("/courses/:id", async (req, res) => { + + try { + const { id } = req.params; + + // find a course based on it's id + const course = await CourseModel.findById(id); + res.send(course); + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + +}) + + +// Get all sections from course +router.get("/courses/:id/sections/all", async (req, res) => { + + try { + const { id } = req.params; + + // find all sections based on a course's id + const sections = await SectionModel.find({ parentCourse: id} ); + + res.send(sections); + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + +}); + +// Get a specififc section +router.get("/courses/:courseId/sections/:sectionId", async (req, res) => { + + try{ + const { courseId, sectionId } = req.params; + + // find a specific section within the given course by both IDs + const section = await SectionModel.findOne({ parentCourse: courseId, _id: sectionId }); + res.send(section); + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + +}); + +// Get all excercies from a section +router.get("/courses/:courseId/sections/:sectionId/exercises/all", async (req, res) => { + + try { + const { courseId, sectionId } = req.params; + + // find a specific section within the given course by both IDs + const exercises = await ExerciseModel.find({ parentSection: sectionId }); + res.send(exercises); + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + +}); + +/*** SUBSCRIPTION ROUTES ***/ + // Subscribe to course +router.post("/courses/:id/subscribe", async (req, res) => { -router.post("/course/subscribe", async (req, res) => { - const { user_id, course_id } = req.body; + try { + const { id } = req.params; + const { user_id} = req.body; + + // find user based on id, and add the course's id to the user's subscriptions field + (await User.findOneAndUpdate( + { _id: user_id }, + { $push: { subscriptions: id} })) + .save; - (await User.findOneAndUpdate( - { _id: user_id }, - { $push: { subscriptions: course_id} })) - .save; + let user = await User.findById(user_id); + res.send(user) - let user = await User.findById(user_id); - res.send(user) + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } }); // Unsubscribe to course -router.post("/course/unsubscribe", async (req, res) => { - const { user_id, course_id} = req.body; +router.post("/courses/:id/unsubscribe", async (req, res) => { + + try { + const { id } = req.params; + const { user_id} = req.body; - (await User.findOneAndUpdate( - { _id: user_id }, - { $pull: { subscriptions: course_id} })) - .save; + // find user based on id, and remove the course's id from the user's subscriptions field + (await User.findOneAndUpdate( + { _id: user_id }, + { $pull: { subscriptions: id} })) + .save; + + let user = await User.findById(user_id); + res.send(user) - let user = await User.findById(user_id); - res.send(user) + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } }); // Get users subscriptions -router.get("/user/subscription/all", async (req, res) => { +router.get("/user/subscriptions/all", async (req, res) => { try { const { user_id } = req.query; @@ -545,40 +638,18 @@ router.get("/user/subscription/all", async (req, res) => { }); -// get all sections from course -router.get("/course/:id/sections/all", async (req, res) => { - const { id } = req.params; // destructure params - const sections = await SectionModel.find({ parentCourse: id} ); - - res.send(sections); -}); - -// get specififc course -router.get("/course/:courseId/section/:sectionId", async (req, res) => { - const { courseId, sectionId } = req.params; // destructure params - - // Find a specific section within the given course by both IDs - const section = await SectionModel.findOne({ parentCourse: courseId, _id: sectionId }); - res.send(section); -}); - -// get all excercies from a section -router.get("/course/:courseId/section/:sectionId/exercises/all", async (req, res) => { - const { courseId, sectionId } = req.params; // destructure params - - // Find a specific section within the given course by both IDs - const exercises = await ExerciseModel.find({ parentSection: sectionId }); - res.send(exercises); -}); -// checks if user is subscribed to a specific course +// Checks if user is subscribed to a specific course router.get('/user', async (req, res) => { try { const { course_id, user_id } = req.query; + + // checks if the course id exist in the users subscriptions field const user = await User.findOne({ _id: user_id, subscriptions: course_id }); + // return true if it exist and false if it does not if(user == null) { res.send("false"); } else { @@ -591,6 +662,4 @@ router.get('/user', async (req, res) => { } }); - - module.exports = router; From 775a470e16bbb4e813e8c4724ee685092318c1ca Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 10 Oct 2023 09:14:17 +0200 Subject: [PATCH 038/129] course fix --- routes/courseRoutes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 4a0f513f..bf30f267 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -614,7 +614,7 @@ router.post("/courses/:id/unsubscribe", async (req, res) => { }); // Get users subscriptions -router.get("/user/subscriptions/all", async (req, res) => { +router.get("/users/subscriptions/all", async (req, res) => { try { const { user_id } = req.query; @@ -640,7 +640,7 @@ router.get("/user/subscriptions/all", async (req, res) => { // Checks if user is subscribed to a specific course -router.get('/user', async (req, res) => { +router.get('/users', async (req, res) => { try { From 47450d059265ffad19a784b361fa8694db65c2ef Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 10 Oct 2023 10:08:54 +0200 Subject: [PATCH 039/129] set up local database --- __tests__/routes/coursesTest.js | 30 ++++++++++++++++++++++++++++++ config/keys.js | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 __tests__/routes/coursesTest.js diff --git a/__tests__/routes/coursesTest.js b/__tests__/routes/coursesTest.js new file mode 100644 index 00000000..ab122f4d --- /dev/null +++ b/__tests__/routes/coursesTest.js @@ -0,0 +1,30 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../../routes/courseRoutes'); // Import your router file here + +const test_courseId = + +const app = express(); +app.use(express.json()); +app.use('/api', router); // Mount the router under '/api' path + +describe('Courses Routes', () => { + // Test the GET /courses/all route + it('should get all courses', async () => { + const response = await request(app).get('/courses/all'); + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Array); + // Add more assertions as needed to check the response data + }); + + // Test the GET /courses/:id route + it('should get a specific course', async () => { + const courseId = 'replace-with-an-existing-course-id'; // Replace with an actual course ID + const response = await request(app).get(`/courses/${courseId}`); + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Object); + // Add more assertions as needed to check the response data + }); + + // Add more test cases for other routes as needed +}); diff --git a/config/keys.js b/config/keys.js index 586fa50b..fb22cc20 100644 --- a/config/keys.js +++ b/config/keys.js @@ -11,7 +11,7 @@ if (process.env.NODE_ENV === "production") { const keys = { googleClientID: process.env.GOOGLE_CLIENT_ID, googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, - mongoURI: process.env.MONGO_URI, + mongoURI: process.env.MONGO_URI_TEST, cookieKey: process.env.COOKIE_KEY, bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, TOKEN_SECRET: process.env.TOKEN_SECRET, From 7a46252ceb432a0cf2b28733e1b257b9f05dc919 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 10 Oct 2023 10:36:32 +0200 Subject: [PATCH 040/129] Update coursesTest.js --- __tests__/routes/coursesTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/routes/coursesTest.js b/__tests__/routes/coursesTest.js index ab122f4d..d4bdc3d0 100644 --- a/__tests__/routes/coursesTest.js +++ b/__tests__/routes/coursesTest.js @@ -2,7 +2,7 @@ const request = require('supertest'); const express = require('express'); const router = require('../../routes/courseRoutes'); // Import your router file here -const test_courseId = +const test_courseId = ; const app = express(); app.use(express.json()); From 9f76b23dd7e29f646645fb4fa1806eda1c27ab67 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 26 Sep 2023 10:01:06 +0200 Subject: [PATCH 041/129] Update userRoutes.js --- routes/userRoutes.js | 73 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 84ccefaf..f8e81728 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -6,14 +6,14 @@ const { User } = require('../models/User'); // Middlewares const requireLogin = require('../middlewares/requireLogin'); -router.delete('/delete/:id', requireLogin, async (req, res) => { - try { - // Get the authenticated user's ID from req.user.id - const { id } = req.params; +router.delete("/delete/:id", requireLogin, async (req, res) => { + try { + // Get the authenticated user's ID from req.user.id + const { id } = req.params; - // Use Mongoose to find and delete the user by ID - console.log('Deleting user with ID:', id); - await User.findByIdAndDelete(id); + // Use Mongoose to find and delete the user by ID + console.log("Deleting user with ID:", id) + await User.findByIdAndDelete(id); // Send a success response res.status(200).json({ message: 'User deleted successfully' }); @@ -24,5 +24,64 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { } }); +// Update User Email route +router.put("/update-email/:id", requireLogin, async (req, res) => { + try { + // Get the authenticated user's ID from req.user.id + const { id } = req.params; + + // Get the new email from the request body + const { newEmail } = req.body; + + // Use Mongoose to find the user by ID and update the email + const updatedUser = await User.findByIdAndUpdate( + id, + { email: newEmail }, + { new: true } // This ensures that the updated user document is returned + ); + + if (!updatedUser) { + // User with the specified ID was not found + return res.status(404).json({ error: "User not found" }); + } + + // Send the updated user data as a response + res.status(200).json({ message: "Email updated successfully", user: updatedUser }); + } catch (error) { + // Handle any errors and send an error response + console.error("Error updating email:", error); + res.status(500).json({ error: "An error occurred while updating the email" }); + } +}); + +// Update User Name route +router.put("/update-name/:id", requireLogin, async (req, res) => { + try { + // Get the authenticated user's ID from req.user.id + const { id } = req.params; + + // Get the new name from the request body + const { newName } = req.body; + + // Use Mongoose to find the user by ID and update the name + const updatedUser = await User.findByIdAndUpdate( + id, + { name: newName }, + { new: true } // This ensures that the updated user document is returned + ); + + if (!updatedUser) { + // User with the specified ID was not found + return res.status(404).json({ error: "User not found" }); + } + + // Send the updated user data as a response + res.status(200).json({ message: "Name updated successfully", user: updatedUser }); + } catch (error) { + // Handle any errors and send an error response + console.error("Error updating name:", error); + res.status(500).json({ error: "An error occurred while updating the name" }); + } +}); module.exports = router; \ No newline at end of file From 11c9da7ee0d7852e2d28c76cf9691d7120556d41 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 26 Sep 2023 10:41:42 +0200 Subject: [PATCH 042/129] Update README.md --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e3cc5b69..44f38161 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,12 @@ Below is a short guide explaining how to set up and run Educado in development m - run `node --version` - if version == v16.14.2, then all is good :) - if version is NOT v16.14.2 then do the following - - Ensure that you have npm installed - - run `npm install -g n` to install node version manager - - when installed, run `n stable`, to install latest stable version of node + - Find the newest stable node version on the web and download it ### Setting up local repository -- Go to GitLab page and clone (with ssh) the Colibri repository -- Create dev.js file for development keys in /config directory -- Get dev keys from Daniel/Jacob and insert into dev.js file and save +- Clone the repository +- Create .env file for development keys in /config directory +- Get dev keys from Daniel/Jacob and insert into .env file and save The dev keys contain the following values: 1. googleClientID 2. googleClientSecret @@ -35,15 +33,12 @@ The dev keys contain the following values: 5. s3 Bucket name ### Installing node dependencies -- Navivate into the cloned repository +- Navigate into the cloned repository - In root folder of the repo run `npm install` -- Navigate into the client-web directory -- Run `npm install` again - ### Run app in development mode - Run `npm run dev` to start application -- By default the web-client runs on ://localhohst:3000 +- By default the web-client runs on ://localhohst:8888 - ...and rest api runs on ://localhost:8888 - Local proxy (from client-web) handles communnication link between them From 06a3b1d54358181a00e7243a52ec3516e632acc6 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 26 Sep 2023 10:50:07 +0200 Subject: [PATCH 043/129] Update README.md --- README.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 44f38161..fddbf703 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,14 @@ Below is a short guide explaining how to set up and run Educado in development m - run `node --version` - if version == v16.14.2, then all is good :) - if version is NOT v16.14.2 then do the following - - Find the newest stable node version on the web and download it + - Ensure that you have npm installed + - run `npm install -g n` to install node version manager + - when installed, run `n stable`, to install latest stable version of node ### Setting up local repository -- Clone the repository -- Create .env file for development keys in /config directory -- Get dev keys from Daniel/Jacob and insert into .env file and save +- Go to GitLab page and clone (with ssh) the Colibri repository +- Create dev.js file for development keys in /config directory +- Get dev keys from Daniel/Jacob and insert into dev.js file and save The dev keys contain the following values: 1. googleClientID 2. googleClientSecret @@ -33,12 +35,23 @@ The dev keys contain the following values: 5. s3 Bucket name ### Installing node dependencies -- Navigate into the cloned repository +- Navivate into the cloned repository - In root folder of the repo run `npm install` +- Navigate into the client-web directory +- Run `npm install` again + +### Setting up AWS CLI +- Use AWS install guide to install on your OS + - [https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html] +- Get AWS keys (access key ID and secret access key) from Daniel/Jacob +- Run `aws configure` and insert access key and secret key +- Set two other potions to 'none' + - Default region name [None]: + - Default output format [None]: ### Run app in development mode - Run `npm run dev` to start application -- By default the web-client runs on ://localhohst:8888 +- By default the web-client runs on ://localhohst:3000 - ...and rest api runs on ://localhost:8888 - Local proxy (from client-web) handles communnication link between them From 2e920f36c20eeb482cdfc52c43493dfd1b465c0d Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 27 Sep 2023 10:43:59 +0200 Subject: [PATCH 044/129] Update .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0b9e5d61..794fd161 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ node_modules -config/dev.js -config/gcp_service.json *.DS_Store package-lock.json -dev.js # enviroment .env From b7dcc1f2fd22cb7ab0da6302d0e5e1f4b6a044df Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 27 Sep 2023 12:16:27 +0200 Subject: [PATCH 045/129] More tests work now 2 tests are still failing --- __tests__/fixtures/fakeUser.js | 17 ++++++++-------- __tests__/setup/globalConfigMongo.json | 2 +- users/index.js | 10 ++++----- users/user.js | 28 +++++++++++++++----------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 84b0ac99..819cdffc 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -2,11 +2,12 @@ const { encrypt } = require('../../helpers/password'); module.exports = function makeFakeUser() { - return { - email: 'fake@gmail.com', - password: encrypt('ABC123456!'), - googleID: '1234567891011', - joinedAt: new Date(), - modifiedAt: new Date() - }; -}; + return { + email: "fake@gmail.com", + password: encrypt('ABC123456!'), + googleID: "1234567891011", + joinedAt: new Date(), + modifiedAt: new Date(), + firstName: "Fake first name", + lastName: "Fake last name" + } diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index b3154bb1..b2653f71 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:51309/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:57000/"} diff --git a/users/index.js b/users/index.js index 2f933033..436885b2 100644 --- a/users/index.js +++ b/users/index.js @@ -1,9 +1,9 @@ -const { UserModel } = require('../models/User'); -const Email = require('../helpers/email'); -const Password = require('../helpers/password'); +const { User } = require('../models/User') +const Email = require('../helpers/email') +const Password = require('../helpers/password') -const makeUserList = require('../users/userList'); -const userList = makeUserList(UserModel); +const makeUserList = require('../users/userList') +const userList = makeUserList(User) const buildMakeUser = require('../users/user'); const makeUser = buildMakeUser({ Email, Password }); diff --git a/users/user.js b/users/user.js index a41e5753..a6a83080 100644 --- a/users/user.js +++ b/users/user.js @@ -1,11 +1,13 @@ module.exports = function buildMakeUser({ Email, Password }) { - return function makeUser({ - email, - password, - joinedAt = new Date(), - modifiedAt = new Date() - } = {}) { + return function makeUser({ + email, + password, + joinedAt = new Date(), + modifiedAt = new Date(), + firstName = "Fake first name", + lastName = "Fake last name" + } = {}) { if (!Email.isValid(email)) throw new Error('User must have a valid email'); @@ -15,12 +17,14 @@ module.exports = function buildMakeUser({ Email, Password }) { const encrypted = Password.encrypt(password); - return Object.freeze({ - email: email, - password: encrypted, - joinedAt: joinedAt, - modifiedAt: modifiedAt - }); + return Object.freeze({ + email: email, + password: encrypted, + joinedAt: joinedAt, + modifiedAt: modifiedAt, + firstName: firstName, + lastName: lastName + }) }; }; \ No newline at end of file From d69f8b4ed7113f74ebf5d558a8447add51a23a44 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 27 Sep 2023 16:29:25 +0200 Subject: [PATCH 046/129] All tests passes --- auth/authHandlerspec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/authHandlerspec.js b/auth/authHandlerspec.js index 103bce8e..a758c2bf 100644 --- a/auth/authHandlerspec.js +++ b/auth/authHandlerspec.js @@ -14,7 +14,7 @@ describe('Authentication Handler', () => { const user = makeUser(fakeUser) await userList.add(user) - + const result = await authHandler.authenticate(fakeUser) expect(result.accessToken).toMatch('ey') From f1e5d5e25fb4fc424b7b74ab7ce845d3b6c0317c Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 27 Sep 2023 17:53:14 +0200 Subject: [PATCH 047/129] update name and email now tested --- __tests__/fixtures/fakeUser.js | 4 +++ users/userList.js | 49 +++++++++++++++++++++++----------- users/userListtest.js | 27 ++++++++++++++----- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 819cdffc..5e86b4ef 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -1,8 +1,11 @@ const { encrypt } = require('../../helpers/password'); +const mongoose = require('mongoose'); module.exports = function makeFakeUser() { + const objectId = new mongoose.Types.ObjectId(); return { + id: objectId.toString(), // Convert the ObjectId to a string email: "fake@gmail.com", password: encrypt('ABC123456!'), googleID: "1234567891011", @@ -11,3 +14,4 @@ module.exports = function makeFakeUser() { firstName: "Fake first name", lastName: "Fake last name" } +} diff --git a/users/userList.js b/users/userList.js index 1904d08e..950012db 100644 --- a/users/userList.js +++ b/users/userList.js @@ -1,23 +1,40 @@ +const email = require("../helpers/email"); + module.exports = function makeUserList(db_model) { + return Object.freeze({ + add, + remove, + findOneByEmail, + findOneById, + updateEmail, + updateName, + }); - return Object.freeze({ - add, - remove, - findOneByEmail, - }); + async function findOneById(id) { + return await db_model.findById(id); + } - async function add(user) { - return await db_model.create(user); - } + async function add(user) { + return await db_model.create(user); + } - async function remove(user = {}) { - const results = await db_model.deleteMany(user); + async function remove(user = {}) { + const results = await db_model.deleteMany(user); + return results.deletedCount; + } - return results.deletedCount; - } + async function findOneByEmail(email) { + return await db_model.findOne({ email: email }); + } - async function findOneByEmail(email) { - return await db_model.findOne({ email: email }); - } + async function updateEmail(email, newEmail) { + const updatedUser = await db_model.findOneAndUpdate({email: email}, {email: newEmail}, {new: true}) + return updatedUser; + } -}; \ No newline at end of file + async function updateName(name, newName) { + const updatedUser = await db_model.findOneAndUpdate({name: name}, {name: newName}, {new: true}) + return updatedUser; + } + +}; diff --git a/users/userListtest.js b/users/userListtest.js index 4f9269f4..02ae8d5e 100644 --- a/users/userListtest.js +++ b/users/userListtest.js @@ -1,7 +1,7 @@ -/*const connectDb = require('../__tests__/fixtures/db') - +const mongoose = require('mongoose'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const connectDb = require('../__tests__/fixtures/db') const makeFakeUser = require("../__tests__/fixtures/fakeUser") - const { userList } = require(".") @@ -23,7 +23,6 @@ describe("User List", () => { const removedCount = await userList.remove(added) expect(removedCount).toBe(1) - }) it("finds a user by email", async () => { @@ -35,8 +34,24 @@ describe("User List", () => { expect(found).not.toBeNull() expect(found.email).toBe(fakeUser.email) - }) + it("can update a user's email", async () => { + const fakeUser = makeFakeUser(); + await userList.add(fakeUser); + + const updatedUser = await userList.updateEmail(fakeUser.email, "newemail@example.com"); + + expect(updatedUser.email).toBe("newemail@example.com"); + }); + + it("can update a user's name", async () => { + const fakeUser = makeFakeUser(); + await userList.add(fakeUser); + + const updatedUser = await userList.updateName(fakeUser.name, "New Name"); + + expect(updatedUser.name).toBe("New Name"); + }); + }) -*/ \ No newline at end of file From 690683cbe17f8bdc5d7b36d4ff32f40f81b335c8 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 27 Sep 2023 17:56:44 +0200 Subject: [PATCH 048/129] Skipped test case is commented out --- .../controller/contentCreatorApplicationController.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/content-creator-applications/controller/contentCreatorApplicationController.spec.js b/applications/content-creator-applications/controller/contentCreatorApplicationController.spec.js index 4457f5f9..0e1c34c9 100644 --- a/applications/content-creator-applications/controller/contentCreatorApplicationController.spec.js +++ b/applications/content-creator-applications/controller/contentCreatorApplicationController.spec.js @@ -71,8 +71,8 @@ describe('Content Creator Application Controller', () => { expect(response.success).toBe(true); expect(response.data.approved).toBe(true); - }); - xit('declines a single content creator application', async () => { }); + }) + //xit('declines a single content creator application', async () => { }) afterAll(async () => { await mongoose.connection.close(); From e9fd47e53244a82ba1f52fcbfd142051e3006e1a Mon Sep 17 00:00:00 2001 From: Mafusn Date: Thu, 28 Sep 2023 09:15:22 +0200 Subject: [PATCH 049/129] Update userList.js --- users/userList.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/users/userList.js b/users/userList.js index 950012db..c27e55b0 100644 --- a/users/userList.js +++ b/users/userList.js @@ -5,15 +5,10 @@ module.exports = function makeUserList(db_model) { add, remove, findOneByEmail, - findOneById, updateEmail, updateName, }); - async function findOneById(id) { - return await db_model.findById(id); - } - async function add(user) { return await db_model.create(user); } From 23ba89091c5489487db1541b76077b3be4638f8e Mon Sep 17 00:00:00 2001 From: Mafusn Date: Fri, 29 Sep 2023 12:35:24 +0200 Subject: [PATCH 050/129] First & last name added to user --- routes/userRoutes.js | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index f8e81728..a162c634 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -54,19 +54,19 @@ router.put("/update-email/:id", requireLogin, async (req, res) => { } }); -// Update User Name route -router.put("/update-name/:id", requireLogin, async (req, res) => { +// Update User first name route +router.put("/update-first_name/:id", requireLogin, async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; // Get the new name from the request body - const { newName } = req.body; + const { newFirstName } = req.body; // Use Mongoose to find the user by ID and update the name const updatedUser = await User.findByIdAndUpdate( id, - { name: newName }, + { firstName: newFirstName }, { new: true } // This ensures that the updated user document is returned ); @@ -76,11 +76,41 @@ router.put("/update-name/:id", requireLogin, async (req, res) => { } // Send the updated user data as a response - res.status(200).json({ message: "Name updated successfully", user: updatedUser }); + res.status(200).json({ message: "First name updated successfully", user: updatedUser }); } catch (error) { // Handle any errors and send an error response - console.error("Error updating name:", error); - res.status(500).json({ error: "An error occurred while updating the name" }); + console.error("Error updating first name:", error); + res.status(500).json({ error: "An error occurred while updating the first name" }); + } +}); + +// Update User last name route +router.put("/update-last_name/:id", requireLogin, async (req, res) => { + try { + // Get the authenticated user's ID from req.user.id + const { id } = req.params; + + // Get the new name from the request body + const { newLastName } = req.body; + + // Use Mongoose to find the user by ID and update the name + const updatedUser = await User.findByIdAndUpdate( + id, + { lastName: newLastName }, + { new: true } // This ensures that the updated user document is returned + ); + + if (!updatedUser) { + // User with the specified ID was not found + return res.status(404).json({ error: "User not found" }); + } + + // Send the updated user data as a response + res.status(200).json({ message: "Last name updated successfully", user: updatedUser }); + } catch (error) { + // Handle any errors and send an error response + console.error("Error updating last name:", error); + res.status(500).json({ error: "An error occurred while updating the last name" }); } }); From 7a9f6288b507c69c32fcbfb1e16eee1c15e51535 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Fri, 29 Sep 2023 12:54:11 +0200 Subject: [PATCH 051/129] Update first & last name tested --- users/userList.js | 14 ++++++++++---- users/userListtest.js | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/users/userList.js b/users/userList.js index c27e55b0..02754cb0 100644 --- a/users/userList.js +++ b/users/userList.js @@ -6,7 +6,8 @@ module.exports = function makeUserList(db_model) { remove, findOneByEmail, updateEmail, - updateName, + updateFirstName, + updateLastName, }); async function add(user) { @@ -27,9 +28,14 @@ module.exports = function makeUserList(db_model) { return updatedUser; } - async function updateName(name, newName) { - const updatedUser = await db_model.findOneAndUpdate({name: name}, {name: newName}, {new: true}) + async function updateFirstName(firstName, newFirstName) { + const updatedUser = await db_model.findOneAndUpdate({firstName: firstName}, {firstName: newFirstName}, {new: true}) return updatedUser; } - + + async function updateLastName(lastName, newLastName) { + const updatedUser = await db_model.findOneAndUpdate({lastName: lastName}, {lastName: newLastName}, {new: true}) + return updatedUser; + } + }; diff --git a/users/userListtest.js b/users/userListtest.js index 02ae8d5e..d3b5eab9 100644 --- a/users/userListtest.js +++ b/users/userListtest.js @@ -45,13 +45,22 @@ describe("User List", () => { expect(updatedUser.email).toBe("newemail@example.com"); }); - it("can update a user's name", async () => { + it("can update a user's first name", async () => { const fakeUser = makeFakeUser(); await userList.add(fakeUser); - const updatedUser = await userList.updateName(fakeUser.name, "New Name"); + const updatedUser = await userList.updateFirstName(fakeUser.firstName, "New First Name"); - expect(updatedUser.name).toBe("New Name"); + expect(updatedUser.firstName).toBe("New First Name"); + }); + + it("can update a user's last name", async () => { + const fakeUser = makeFakeUser(); + await userList.add(fakeUser); + + const updatedUser = await userList.updateLastName(fakeUser.lastName, "New Last Name"); + + expect(updatedUser.lastName).toBe("New Last Name"); }); }) From 6c26e6e118c8e57afafdb1db9893d38ef655ed6a Mon Sep 17 00:00:00 2001 From: Mafusn Date: Mon, 2 Oct 2023 11:25:10 +0200 Subject: [PATCH 052/129] Tests a update functions in userRoute --- __tests__/fixtures/db.js | 27 +++++----- __tests__/fixtures/fakeUser.js | 3 -- routes/userRoutes.js | 6 +-- routes/userRoutes.spec.js | 99 ++++++++++++++++++++++++++++++++++ users/userListtest.js | 4 +- 5 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 routes/userRoutes.spec.js diff --git a/__tests__/fixtures/db.js b/__tests__/fixtures/db.js index dd4ae47d..1e54fa8f 100644 --- a/__tests__/fixtures/db.js +++ b/__tests__/fixtures/db.js @@ -3,18 +3,17 @@ const mongoose = require('mongoose'); let connection, db; module.exports = async function connectDb() { - - connection = - connection || - await mongoose.connect( - global.__MONGO_URI__, - { - useNewUrlParser: true, - useFindAndModify: false - } - ); - //db = db || connection.db(global.MONGO_DB_NAME) - db = db || connection.connection.db; // Assign the database object + connection = + connection || + await mongoose.connect( + global.__MONGO_URI__, + { + useNewUrlParser: true, + useFindAndModify: false + } + ) + //db = db || connection.db(global.__MONGO_DB_NAME__) + db = db || connection.connection.db; // Assign the database object - return db; // Return the database object -}; \ No newline at end of file + return db; // Return the database object +} diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 5e86b4ef..ad854788 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -2,10 +2,7 @@ const { encrypt } = require('../../helpers/password'); const mongoose = require('mongoose'); module.exports = function makeFakeUser() { - const objectId = new mongoose.Types.ObjectId(); - return { - id: objectId.toString(), // Convert the ObjectId to a string email: "fake@gmail.com", password: encrypt('ABC123456!'), googleID: "1234567891011", diff --git a/routes/userRoutes.js b/routes/userRoutes.js index a162c634..8deeee18 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -25,7 +25,7 @@ router.delete("/delete/:id", requireLogin, async (req, res) => { }); // Update User Email route -router.put("/update-email/:id", requireLogin, async (req, res) => { +router.put("/update-email/:id", async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; @@ -55,7 +55,7 @@ router.put("/update-email/:id", requireLogin, async (req, res) => { }); // Update User first name route -router.put("/update-first_name/:id", requireLogin, async (req, res) => { +router.put("/update-first_name/:id", async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; @@ -85,7 +85,7 @@ router.put("/update-first_name/:id", requireLogin, async (req, res) => { }); // Update User last name route -router.put("/update-last_name/:id", requireLogin, async (req, res) => { +router.put("/update-last_name/:id", async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; diff --git a/routes/userRoutes.spec.js b/routes/userRoutes.spec.js new file mode 100644 index 00000000..22a3c083 --- /dev/null +++ b/routes/userRoutes.spec.js @@ -0,0 +1,99 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../routes/userRoutes'); // Import your router file here +const connectDb = require('../__tests__/fixtures/db') +const makeFakeUser = require('../__tests__/fixtures/fakeUser') +const mongoose = require('mongoose'); + +const app = express(); +app.use(express.json()); +app.use('/api/user', router); // Mount the router under '/api' path + +// Start the Express app on a specific port for testing +const PORT = 5000; // Choose a port for testing +const server = app.listen(PORT, () => { + console.log(`Express server is running on port ${PORT}`); +}); + +const fakeUser = makeFakeUser(); + +describe('Update User Email Route', () => { + + let db; // Store the database connection + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + // Insert the fake user into the database + await db.collection('users').insertOne(fakeUser); + }); + + it('updates user email successfully', async () => { + const newEmail = 'newemail@example.com'; + + // Send a PUT request to the update-email endpoint + const response = await request('http://localhost:5000') + .put('/api/user/update-email/' + fakeUser._id) + .send({ newEmail }) + .expect(200); + + // Verify the response body + expect(response.body.message).toBe('Email updated successfully'); + expect(response.body.user.email).toBe(newEmail); + }); + + it('handles user not found error for update-email', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + const newEmail = 'newemail@example.com'; + + // Send a PUT request to the update-email endpoint with a non-existent user ID + await request('http://localhost:5000') + .put('/api/user/update-email/' + nonExistentUserId) + .send({ newEmail }) + .expect(404); + }); + + it('updates user first name successfully', async () => { + // Send a PUT request to the update-first_name endpoint using the inserted user's ID + await request('http://localhost:5000') + .put(`/api/user/update-first_name/${fakeUser._id}`) + .send({ newFirstName: 'NewFirstName' }) + .expect(200); + }); + + it('handles user not found error for update-first_name', async () => { + // Create a non-existent user ID + const nonExistentUserId = new mongoose.Types.ObjectId(); + + // Send a PUT request to the update-first_name endpoint with a non-existent user ID + await request('http://localhost:5000') + .put(`/api/user/update-first_name/${nonExistentUserId}`) + .send({ newFirstName: 'NewFirstName' }) + .expect(404); + + // Your assertions and expectations based on the response... + }); + + it('updates user last name successfully', async () => { + // Send a PUT request to the update-last_name endpoint using the inserted user's ID + await request('http://localhost:5000') + .put(`/api/user/update-last_name/${fakeUser._id}`) + .send({ newLastName: 'NewLastName' }) + .expect(200); + }); + + it('handles user not found error for update-last_name', async () => { + // Create a non-existent user ID + const nonExistentUserId = new mongoose.Types.ObjectId(); + + // Send a PUT request to the update-last_name endpoint with a non-existent user ID + await request('http://localhost:5000') + .put(`/api/user/update-last_name/${nonExistentUserId}`) + .send({ newLastName: 'NewLastName' }) + .expect(404); + }); + + afterAll((done) => { + server.close(done); + }); +}); diff --git a/users/userListtest.js b/users/userListtest.js index d3b5eab9..060c87b7 100644 --- a/users/userListtest.js +++ b/users/userListtest.js @@ -1,8 +1,8 @@ const mongoose = require('mongoose'); const { MongoMemoryServer } = require('mongodb-memory-server'); const connectDb = require('../__tests__/fixtures/db') -const makeFakeUser = require("../__tests__/fixtures/fakeUser") -const { userList } = require(".") +const makeFakeUser = require('../__tests__/fixtures/fakeUser') +const { userList } = require('.') describe("User List", () => { From 2535c194087582a8f5a6f5c69ff8250ddbb25ab8 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Mon, 2 Oct 2023 11:49:40 +0200 Subject: [PATCH 053/129] All tests passes --- routes/userRoutes.js | 6 +++--- routes/userRoutes.spec.js | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 8deeee18..a162c634 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -25,7 +25,7 @@ router.delete("/delete/:id", requireLogin, async (req, res) => { }); // Update User Email route -router.put("/update-email/:id", async (req, res) => { +router.put("/update-email/:id", requireLogin, async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; @@ -55,7 +55,7 @@ router.put("/update-email/:id", async (req, res) => { }); // Update User first name route -router.put("/update-first_name/:id", async (req, res) => { +router.put("/update-first_name/:id", requireLogin, async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; @@ -85,7 +85,7 @@ router.put("/update-first_name/:id", async (req, res) => { }); // Update User last name route -router.put("/update-last_name/:id", async (req, res) => { +router.put("/update-last_name/:id", requireLogin, async (req, res) => { try { // Get the authenticated user's ID from req.user.id const { id } = req.params; diff --git a/routes/userRoutes.spec.js b/routes/userRoutes.spec.js index 22a3c083..4c51180d 100644 --- a/routes/userRoutes.spec.js +++ b/routes/userRoutes.spec.js @@ -7,6 +7,14 @@ const mongoose = require('mongoose'); const app = express(); app.use(express.json()); + +// Mock authentication middleware +app.use((req, res, next) => { + // Create a mock user object for testing + req.user = { id: 'mockUserId', /* Add other user properties as needed */ }; + next(); + }); + app.use('/api/user', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing From ec1494d8d7b620caa72c0372c63d8dab36596c4c Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 3 Oct 2023 11:20:12 +0200 Subject: [PATCH 054/129] Update userRoutes.js --- routes/userRoutes.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index a162c634..f27c2ceb 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -8,11 +8,8 @@ const requireLogin = require('../middlewares/requireLogin'); router.delete("/delete/:id", requireLogin, async (req, res) => { try { - // Get the authenticated user's ID from req.user.id const { id } = req.params; - // Use Mongoose to find and delete the user by ID - console.log("Deleting user with ID:", id) await User.findByIdAndDelete(id); // Send a success response @@ -27,13 +24,9 @@ router.delete("/delete/:id", requireLogin, async (req, res) => { // Update User Email route router.put("/update-email/:id", requireLogin, async (req, res) => { try { - // Get the authenticated user's ID from req.user.id const { id } = req.params; - - // Get the new email from the request body const { newEmail } = req.body; - // Use Mongoose to find the user by ID and update the email const updatedUser = await User.findByIdAndUpdate( id, { email: newEmail }, @@ -41,14 +34,11 @@ router.put("/update-email/:id", requireLogin, async (req, res) => { ); if (!updatedUser) { - // User with the specified ID was not found return res.status(404).json({ error: "User not found" }); } - // Send the updated user data as a response res.status(200).json({ message: "Email updated successfully", user: updatedUser }); } catch (error) { - // Handle any errors and send an error response console.error("Error updating email:", error); res.status(500).json({ error: "An error occurred while updating the email" }); } @@ -57,13 +47,9 @@ router.put("/update-email/:id", requireLogin, async (req, res) => { // Update User first name route router.put("/update-first_name/:id", requireLogin, async (req, res) => { try { - // Get the authenticated user's ID from req.user.id const { id } = req.params; - - // Get the new name from the request body const { newFirstName } = req.body; - // Use Mongoose to find the user by ID and update the name const updatedUser = await User.findByIdAndUpdate( id, { firstName: newFirstName }, @@ -71,14 +57,11 @@ router.put("/update-first_name/:id", requireLogin, async (req, res) => { ); if (!updatedUser) { - // User with the specified ID was not found return res.status(404).json({ error: "User not found" }); } - // Send the updated user data as a response res.status(200).json({ message: "First name updated successfully", user: updatedUser }); } catch (error) { - // Handle any errors and send an error response console.error("Error updating first name:", error); res.status(500).json({ error: "An error occurred while updating the first name" }); } @@ -87,13 +70,9 @@ router.put("/update-first_name/:id", requireLogin, async (req, res) => { // Update User last name route router.put("/update-last_name/:id", requireLogin, async (req, res) => { try { - // Get the authenticated user's ID from req.user.id const { id } = req.params; - - // Get the new name from the request body const { newLastName } = req.body; - // Use Mongoose to find the user by ID and update the name const updatedUser = await User.findByIdAndUpdate( id, { lastName: newLastName }, @@ -101,14 +80,11 @@ router.put("/update-last_name/:id", requireLogin, async (req, res) => { ); if (!updatedUser) { - // User with the specified ID was not found return res.status(404).json({ error: "User not found" }); } - // Send the updated user data as a response res.status(200).json({ message: "Last name updated successfully", user: updatedUser }); } catch (error) { - // Handle any errors and send an error response console.error("Error updating last name:", error); res.status(500).json({ error: "An error occurred while updating the last name" }); } From 1a02c2ad912943fbc54d24e2f77ea7375e198ec6 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 3 Oct 2023 11:30:55 +0200 Subject: [PATCH 055/129] Slettet comments --- routes/userRoutes.spec.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/routes/userRoutes.spec.js b/routes/userRoutes.spec.js index 4c51180d..81db3329 100644 --- a/routes/userRoutes.spec.js +++ b/routes/userRoutes.spec.js @@ -27,12 +27,11 @@ const fakeUser = makeFakeUser(); describe('Update User Email Route', () => { - let db; // Store the database connection + let db; beforeAll(async () => { - db = await connectDb(); // Connect to the database + db = await connectDb(); - // Insert the fake user into the database await db.collection('users').insertOne(fakeUser); }); @@ -45,7 +44,6 @@ describe('Update User Email Route', () => { .send({ newEmail }) .expect(200); - // Verify the response body expect(response.body.message).toBe('Email updated successfully'); expect(response.body.user.email).toBe(newEmail); }); @@ -54,7 +52,6 @@ describe('Update User Email Route', () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; - // Send a PUT request to the update-email endpoint with a non-existent user ID await request('http://localhost:5000') .put('/api/user/update-email/' + nonExistentUserId) .send({ newEmail }) @@ -62,7 +59,6 @@ describe('Update User Email Route', () => { }); it('updates user first name successfully', async () => { - // Send a PUT request to the update-first_name endpoint using the inserted user's ID await request('http://localhost:5000') .put(`/api/user/update-first_name/${fakeUser._id}`) .send({ newFirstName: 'NewFirstName' }) @@ -70,20 +66,16 @@ describe('Update User Email Route', () => { }); it('handles user not found error for update-first_name', async () => { - // Create a non-existent user ID const nonExistentUserId = new mongoose.Types.ObjectId(); - // Send a PUT request to the update-first_name endpoint with a non-existent user ID await request('http://localhost:5000') .put(`/api/user/update-first_name/${nonExistentUserId}`) .send({ newFirstName: 'NewFirstName' }) .expect(404); - // Your assertions and expectations based on the response... }); it('updates user last name successfully', async () => { - // Send a PUT request to the update-last_name endpoint using the inserted user's ID await request('http://localhost:5000') .put(`/api/user/update-last_name/${fakeUser._id}`) .send({ newLastName: 'NewLastName' }) @@ -91,10 +83,8 @@ describe('Update User Email Route', () => { }); it('handles user not found error for update-last_name', async () => { - // Create a non-existent user ID const nonExistentUserId = new mongoose.Types.ObjectId(); - // Send a PUT request to the update-last_name endpoint with a non-existent user ID await request('http://localhost:5000') .put(`/api/user/update-last_name/${nonExistentUserId}`) .send({ newLastName: 'NewLastName' }) From 789945930ab5819ba61b6b8a09fa959dc3cdedbe Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 3 Oct 2023 12:22:16 +0200 Subject: [PATCH 056/129] Delete user unit testing --- routes/userRoutes.js | 6 +++++- routes/userRoutes.spec.js | 34 ++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index f27c2ceb..7e1c38a9 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -10,7 +10,11 @@ router.delete("/delete/:id", requireLogin, async (req, res) => { try { const { id } = req.params; - await User.findByIdAndDelete(id); + const deletedUser = await User.findByIdAndDelete(id); + + if (!deletedUser) { + return res.status(404).json({ error: "User not found" }); + } // Send a success response res.status(200).json({ message: 'User deleted successfully' }); diff --git a/routes/userRoutes.spec.js b/routes/userRoutes.spec.js index 81db3329..6430c5e0 100644 --- a/routes/userRoutes.spec.js +++ b/routes/userRoutes.spec.js @@ -25,7 +25,7 @@ const server = app.listen(PORT, () => { const fakeUser = makeFakeUser(); -describe('Update User Email Route', () => { +describe('User routes', () => { let db; @@ -76,21 +76,35 @@ describe('Update User Email Route', () => { }); it('updates user last name successfully', async () => { - await request('http://localhost:5000') - .put(`/api/user/update-last_name/${fakeUser._id}`) - .send({ newLastName: 'NewLastName' }) - .expect(200); + await request('http://localhost:5000') + .put(`/api/user/update-last_name/${fakeUser._id}`) + .send({ newLastName: 'NewLastName' }) + .expect(200); }); it('handles user not found error for update-last_name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); + const nonExistentUserId = new mongoose.Types.ObjectId(); - await request('http://localhost:5000') - .put(`/api/user/update-last_name/${nonExistentUserId}`) - .send({ newLastName: 'NewLastName' }) - .expect(404); + await request('http://localhost:5000') + .put(`/api/user/update-last_name/${nonExistentUserId}`) + .send({ newLastName: 'NewLastName' }) + .expect(404); }); + it('deletes user successfully', async () => { + await request('http://localhost:5000') + .delete(`/api/user/delete/${fakeUser._id}`) + .expect(200); + }); + + it('handles user not found error for delete', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + + await request('http://localhost:5000') + .delete(`/api/user/delete/${nonExistentUserId}`) + .expect(404); + }); + afterAll((done) => { server.close(done); }); From 9567d69b06e50412b64937bf9ef3b4e3129beb8e Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 4 Oct 2023 10:34:25 +0200 Subject: [PATCH 057/129] Better covered tests --- routes/userRoutes.spec.js | 51 +++++++++++++++------------------------ 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/routes/userRoutes.spec.js b/routes/userRoutes.spec.js index 6430c5e0..a975db68 100644 --- a/routes/userRoutes.spec.js +++ b/routes/userRoutes.spec.js @@ -1,6 +1,6 @@ const request = require('supertest'); const express = require('express'); -const router = require('../routes/userRoutes'); // Import your router file here +const router = require('../routes/userRoutes'); const connectDb = require('../__tests__/fixtures/db') const makeFakeUser = require('../__tests__/fixtures/fakeUser') const mongoose = require('mongoose'); @@ -10,8 +10,7 @@ app.use(express.json()); // Mock authentication middleware app.use((req, res, next) => { - // Create a mock user object for testing - req.user = { id: 'mockUserId', /* Add other user properties as needed */ }; + req.user = { id: 'mockUserId', /* Needs this for requireLogin middleware */ }; next(); }); @@ -25,12 +24,12 @@ const server = app.listen(PORT, () => { const fakeUser = makeFakeUser(); -describe('User routes', () => { +describe('Update User Email Route', () => { let db; beforeAll(async () => { - db = await connectDb(); + db = await connectDb(); // Connect to the database await db.collection('users').insertOne(fakeUser); }); @@ -38,7 +37,6 @@ describe('User routes', () => { it('updates user email successfully', async () => { const newEmail = 'newemail@example.com'; - // Send a PUT request to the update-email endpoint const response = await request('http://localhost:5000') .put('/api/user/update-email/' + fakeUser._id) .send({ newEmail }) @@ -59,10 +57,13 @@ describe('User routes', () => { }); it('updates user first name successfully', async () => { - await request('http://localhost:5000') - .put(`/api/user/update-first_name/${fakeUser._id}`) - .send({ newFirstName: 'NewFirstName' }) - .expect(200); + const response = await request('http://localhost:5000') + .put(`/api/user/update-first_name/${fakeUser._id}`) + .send({ newFirstName: 'NewFirstName' }) + .expect(200); + + expect(response.body.message).toBe('First name updated successfully'); + expect(response.body.user.firstName).toBe('NewFirstName'); }); it('handles user not found error for update-first_name', async () => { @@ -72,38 +73,26 @@ describe('User routes', () => { .put(`/api/user/update-first_name/${nonExistentUserId}`) .send({ newFirstName: 'NewFirstName' }) .expect(404); - }); it('updates user last name successfully', async () => { - await request('http://localhost:5000') + const response = await request('http://localhost:5000') .put(`/api/user/update-last_name/${fakeUser._id}`) .send({ newLastName: 'NewLastName' }) .expect(200); + + expect(response.body.message).toBe('Last name updated successfully'); + expect(response.body.user.lastName).toBe('NewLastName'); }); it('handles user not found error for update-last_name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - - await request('http://localhost:5000') - .put(`/api/user/update-last_name/${nonExistentUserId}`) - .send({ newLastName: 'NewLastName' }) - .expect(404); - }); + const nonExistentUserId = new mongoose.Types.ObjectId(); - it('deletes user successfully', async () => { - await request('http://localhost:5000') - .delete(`/api/user/delete/${fakeUser._id}`) - .expect(200); + await request('http://localhost:5000') + .put(`/api/user/update-last_name/${nonExistentUserId}`) + .send({ newLastName: 'NewLastName' }) + .expect(404); }); - - it('handles user not found error for delete', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - - await request('http://localhost:5000') - .delete(`/api/user/delete/${nonExistentUserId}`) - .expect(404); - }); afterAll((done) => { server.close(done); From 059899fd4fabe291158b448501854ed74fc00298 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 10:02:58 +0200 Subject: [PATCH 058/129] Revert "Resolved conflicts" This reverts commit 3b4f4fd69fabc8564b9adeae22e4a64df19da886, reversing changes made to 6d0dd36c1418f3c8027e2401635cdabd17cf5106. --- __tests__/fixtures/db.js | 4 +-- middlewares/requireLogin.js | 16 ++++------- models/Components.js | 16 +++++------ models/ContentCreatorApplication.js | 22 +++++++-------- models/User.js | 44 ++++++++++++++--------------- package.json | 20 +++++-------- routes/userRoutes.js | 8 +++--- users/index.js | 6 ++-- users/user.js | 14 ++++----- 9 files changed, 70 insertions(+), 80 deletions(-) diff --git a/__tests__/fixtures/db.js b/__tests__/fixtures/db.js index 1e54fa8f..b911a3ae 100644 --- a/__tests__/fixtures/db.js +++ b/__tests__/fixtures/db.js @@ -1,6 +1,6 @@ -const mongoose = require('mongoose'); +const mongoose = require('mongoose') -let connection, db; +let connection, db module.exports = async function connectDb() { connection = diff --git a/middlewares/requireLogin.js b/middlewares/requireLogin.js index 67a5f7ac..69e7d8cd 100644 --- a/middlewares/requireLogin.js +++ b/middlewares/requireLogin.js @@ -1,11 +1,7 @@ -const { verify } = require('../helpers/token'); - module.exports = (req, res, next) => { - try { - const claims = verify(req.headers.token ?? ''); - next(); - } catch { - // TODO: add updated error messages - return res.status(401).send({ error: 'You must be logged in!' }); - } -}; \ No newline at end of file + if (!req.user) { + return res.status(401).send({ error: "You must be logged in!" }); + } + + next(); +}; diff --git a/models/Components.js b/models/Components.js index df2df1f5..e1a5f126 100644 --- a/models/Components.js +++ b/models/Components.js @@ -1,16 +1,16 @@ // Mongoose model class for Courses -const mongoose = require('mongoose'); +const mongoose = require("mongoose"); const { Schema } = mongoose; // Class description const componentsSchema = new Schema({ - type: String, // Image / Video / Audio / Text - file: String, // AWS URL, if video, audio or image - text: String, // IF component is text - dateCreated: Date, // For all components - dateUpdated: Date, // If its a text component + type: String, // Image / Video / Audio / Text + file: String, // AWS URL, if video, audio or image + text: String, // IF component is text + dateCreated: Date, // For all components + dateUpdated: Date, // If its a text component }); -const ComponentModel = mongoose.model('components', componentsSchema); +const ComponentModel = mongoose.model("components", componentsSchema); -module.exports = { ComponentModel }; \ No newline at end of file +module.exports = { ComponentModel } diff --git a/models/ContentCreatorApplication.js b/models/ContentCreatorApplication.js index c76d30a5..684b766a 100644 --- a/models/ContentCreatorApplication.js +++ b/models/ContentCreatorApplication.js @@ -1,22 +1,22 @@ // Mongoose model class for User -const mongoose = require('mongoose'); +const mongoose = require("mongoose"); const Schema = mongoose.Schema; // Class description const ContentCreatorSchema = new Schema({ - firstName: { type: String, required: true }, - lastName: { type: String, required: true }, - email: { type: String, required: true }, - motivation: { type: String }, - approved: { type: Boolean, default: false }, - rejectionReason: { type: String, required: false }, - createdAt: { type: Date }, - modifiedAt: { type: Date }, + firstName: { type: String, required: true }, + lastName: { type: String, required: true }, + email: { type: String, required: true }, + motivation: { type: String }, + approved: { type: Boolean, default: false }, + rejectionReason: { type: String, required: false }, + createdAt: { type: Date }, + modifiedAt: { type: Date }, }); const ContentCreatorApplication = mongoose.model( - 'Content-Creator-Application', - ContentCreatorSchema + "Content-Creator-Application", + ContentCreatorSchema ); module.exports = { ContentCreatorApplication }; diff --git a/models/User.js b/models/User.js index 83772c10..eaddae50 100644 --- a/models/User.js +++ b/models/User.js @@ -1,5 +1,5 @@ // Mongoose model class for User -const mongoose = require('mongoose'); +const mongoose = require("mongoose") const { Schema } = mongoose; // Class description @@ -53,28 +53,28 @@ const userSchema = new Schema({ * followed by a dot, followed by a sequence of two to four domain * extension letters. */ - return patterns.email.test(email); - }, - message: 'Invalid email' - }, - validate: { - validator: async function(input) { - let users = await UserModel.find({email: input}, function(err,docs){ + return /^[0-9a-zA-Z.]+@[a-zA-Z]+.[a-zA-Z]{2,4}/.test(email); + }, + message: "Invalid email" + }, + validate: { + validator: async function(input) { + let users = await UserModel.find({email: input}, function(err,docs){ - }); - if(users.length){ - return false; - } - return true; - }, - message: 'User email already exists!' - } - }, - password: String, - joinedAt: Date, - modifiedAt: Date + }); + if(users.length){ + return false; + } + return true; + }, + message: 'User email already exists!' + } + }, + password: String, + joinedAt: Date, + modifiedAt: Date }); -const UserModel = mongoose.model('users', userSchema); +const UserModel = mongoose.model("users", userSchema); -module.exports.User = UserModel; +module.exports.User = UserModel diff --git a/package.json b/package.json index 9c4011c2..0c59a402 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,13 @@ "scripts": { "start": "node index.js", "server": "nodemon index.js", - "test": "jest --runInBand", - "dev": "NODE_ENV=development npm run server", - "staging": "NODE_ENV=staging npm run server", - "prod": "NODE_ENV=production npm run server", + "test": "jest --runInBand --forceExit", + "dev": "npm run server", + "prod": "npm run server", "docker": "docker-compose up -d", "docker_down": "docker-compose down", "docker_build": "docker-compose up -d --build", - "docker_clean_db": "docker-compose down && docker volume rm educado-backend_mongodb_data && docker-compose up -d --build mongodb", - "linting": "eslint --ext .js,.jsx,.ts,.tsx .", - "typechecking": "tsc --noEmit" + "docker_clean_db": "docker-compose down && docker volume rm educado-backend_mongodb_data && docker-compose up -d --build mongodb" }, "author": "", "license": "ISC", @@ -34,7 +31,6 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "ejs": "^3.1.5", - "eslint": "^8.50.0", "express": "^4.17.1", "http-proxy-middleware": "^2.0.3", "jsonwebtoken": "^8.5.1", @@ -46,8 +42,7 @@ "nodemon": "^2.0.6", "passport": "^0.6.0", "passport-google-oauth20": "^2.0.0", - "supertest": "^6.3.3", - "prettier": "^3.0.3" + "supertest": "^6.3.3" }, "devDependencies": { "@babel/cli": "^7.2.3", @@ -55,9 +50,8 @@ "@babel/node": "^7.2.2", "@babel/preset-env": "^7.3.4", "babel-jest": "^24.4.0", - "jest": "^29.1.2", - "mongodb-memory-server": "^8.9.3", - "supertest": "^6.3.3" + "jest": "^29.7.0", + "mongodb-memory-server": "^8.9.3" }, "jest": { "verbose": true, diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 7e1c38a9..94784bca 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -1,10 +1,10 @@ -const router = require('express').Router(); +const router = require("express").Router(); // Models -const { User } = require('../models/User'); +const { User } = require("../models/User"); // Middlewares -const requireLogin = require('../middlewares/requireLogin'); +const requireLogin = require("../middlewares/requireLogin"); router.delete("/delete/:id", requireLogin, async (req, res) => { try { @@ -94,4 +94,4 @@ router.put("/update-last_name/:id", requireLogin, async (req, res) => { } }); -module.exports = router; \ No newline at end of file + module.exports = router; \ No newline at end of file diff --git a/users/index.js b/users/index.js index 436885b2..3992c10d 100644 --- a/users/index.js +++ b/users/index.js @@ -5,7 +5,7 @@ const Password = require('../helpers/password') const makeUserList = require('../users/userList') const userList = makeUserList(User) -const buildMakeUser = require('../users/user'); -const makeUser = buildMakeUser({ Email, Password }); +const buildMakeUser = require('../users/user') +const makeUser = buildMakeUser({ Email, Password }) -module.exports = { makeUser, userList }; \ No newline at end of file +module.exports = { makeUser, userList } \ No newline at end of file diff --git a/users/user.js b/users/user.js index a6a83080..1f464970 100644 --- a/users/user.js +++ b/users/user.js @@ -9,13 +9,13 @@ module.exports = function buildMakeUser({ Email, Password }) { lastName = "Fake last name" } = {}) { - if (!Email.isValid(email)) throw new Error('User must have a valid email'); + if (!Email.isValid(email)) throw new Error("User must have a valid email") - if (!password) throw new Error('User must have a password'); - if (!(password.length >= 8)) throw new Error('Password should be atleast 8 characters long'); - if (password.search('[A-Z]') == -1) throw new Error('Password must contain a capital letter'); + if (!password) throw new Error("User must have a password") + if (!(password.length >= 8)) throw new Error("Password should be atleast 8 characters long") + if (password.search("[A-Z]") == -1) throw new Error("Password must contain a capital letter") - const encrypted = Password.encrypt(password); + const encrypted = Password.encrypt(password) return Object.freeze({ email: email, @@ -26,5 +26,5 @@ module.exports = function buildMakeUser({ Email, Password }) { lastName: lastName }) - }; -}; \ No newline at end of file + } +} \ No newline at end of file From 08fc24d5d2c93b9ae9eb7680681be0d067d51631 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 10:10:57 +0200 Subject: [PATCH 059/129] Fix: user route names --- routes/userRoutes.js | 4 ++-- routes/userRoutes.spec.js | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 94784bca..0f1b5ff5 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -49,7 +49,7 @@ router.put("/update-email/:id", requireLogin, async (req, res) => { }); // Update User first name route -router.put("/update-first_name/:id", requireLogin, async (req, res) => { +router.put("/update-first-name/:id", requireLogin, async (req, res) => { try { const { id } = req.params; const { newFirstName } = req.body; @@ -72,7 +72,7 @@ router.put("/update-first_name/:id", requireLogin, async (req, res) => { }); // Update User last name route -router.put("/update-last_name/:id", requireLogin, async (req, res) => { +router.put("/update-last-name/:id", requireLogin, async (req, res) => { try { const { id } = req.params; const { newLastName } = req.body; diff --git a/routes/userRoutes.spec.js b/routes/userRoutes.spec.js index a975db68..6fb9c423 100644 --- a/routes/userRoutes.spec.js +++ b/routes/userRoutes.spec.js @@ -58,7 +58,7 @@ describe('Update User Email Route', () => { it('updates user first name successfully', async () => { const response = await request('http://localhost:5000') - .put(`/api/user/update-first_name/${fakeUser._id}`) + .put(`/api/user/update-first-name/${fakeUser._id}`) .send({ newFirstName: 'NewFirstName' }) .expect(200); @@ -66,18 +66,18 @@ describe('Update User Email Route', () => { expect(response.body.user.firstName).toBe('NewFirstName'); }); - it('handles user not found error for update-first_name', async () => { + it('handles user not found error for update-first-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request('http://localhost:5000') - .put(`/api/user/update-first_name/${nonExistentUserId}`) + .put(`/api/user/update-first-name/${nonExistentUserId}`) .send({ newFirstName: 'NewFirstName' }) .expect(404); }); it('updates user last name successfully', async () => { const response = await request('http://localhost:5000') - .put(`/api/user/update-last_name/${fakeUser._id}`) + .put(`/api/user/update-last-name/${fakeUser._id}`) .send({ newLastName: 'NewLastName' }) .expect(200); @@ -85,11 +85,11 @@ describe('Update User Email Route', () => { expect(response.body.user.lastName).toBe('NewLastName'); }); - it('handles user not found error for update-last_name', async () => { + it('handles user not found error for update-last-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request('http://localhost:5000') - .put(`/api/user/update-last_name/${nonExistentUserId}`) + .put(`/api/user/update-last-name/${nonExistentUserId}`) .send({ newLastName: 'NewLastName' }) .expect(404); }); From 9290bbda9f87c831f51e55c23005488845495d48 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 10:38:52 +0200 Subject: [PATCH 060/129] Fix: tests moved --- .../routes}/userRoutes.spec.js | 20 +++++++++---------- config/keys.js | 12 +++++------ 2 files changed, 16 insertions(+), 16 deletions(-) rename {routes => __tests__/routes}/userRoutes.spec.js (83%) diff --git a/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js similarity index 83% rename from routes/userRoutes.spec.js rename to __tests__/routes/userRoutes.spec.js index 6fb9c423..0a8707af 100644 --- a/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -1,8 +1,8 @@ const request = require('supertest'); const express = require('express'); -const router = require('../routes/userRoutes'); -const connectDb = require('../__tests__/fixtures/db') -const makeFakeUser = require('../__tests__/fixtures/fakeUser') +const router = require('../../routes/userRoutes'); +const connectDb = require('../fixtures/db') +const makeFakeUser = require('../fixtures/fakeUser') const mongoose = require('mongoose'); const app = express(); @@ -17,7 +17,7 @@ app.use((req, res, next) => { app.use('/api/user', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing -const PORT = 5000; // Choose a port for testing +const PORT = 5022; // Choose a port for testing const server = app.listen(PORT, () => { console.log(`Express server is running on port ${PORT}`); }); @@ -37,7 +37,7 @@ describe('Update User Email Route', () => { it('updates user email successfully', async () => { const newEmail = 'newemail@example.com'; - const response = await request('http://localhost:5000') + const response = await request(`http://localhost:${PORT}`) .put('/api/user/update-email/' + fakeUser._id) .send({ newEmail }) .expect(200); @@ -50,14 +50,14 @@ describe('Update User Email Route', () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; - await request('http://localhost:5000') + await request(`http://localhost:${PORT}`) .put('/api/user/update-email/' + nonExistentUserId) .send({ newEmail }) .expect(404); }); it('updates user first name successfully', async () => { - const response = await request('http://localhost:5000') + const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${fakeUser._id}`) .send({ newFirstName: 'NewFirstName' }) .expect(200); @@ -69,14 +69,14 @@ describe('Update User Email Route', () => { it('handles user not found error for update-first-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); - await request('http://localhost:5000') + await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${nonExistentUserId}`) .send({ newFirstName: 'NewFirstName' }) .expect(404); }); it('updates user last name successfully', async () => { - const response = await request('http://localhost:5000') + const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${fakeUser._id}`) .send({ newLastName: 'NewLastName' }) .expect(200); @@ -88,7 +88,7 @@ describe('Update User Email Route', () => { it('handles user not found error for update-last-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); - await request('http://localhost:5000') + await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${nonExistentUserId}`) .send({ newLastName: 'NewLastName' }) .expect(404); diff --git a/config/keys.js b/config/keys.js index 5c469bf6..f311a61e 100644 --- a/config/keys.js +++ b/config/keys.js @@ -9,12 +9,12 @@ if (process.env.NODE_ENV === 'production') { // Access the environment variables const keys = { - googleClientID: process.env.GOOGLE_CLIENT_ID, - googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, - mongoURI: process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production' ? process.env.MONGO_URI : process.env.MONGO_URI_TEST, - cookieKey: process.env.COOKIE_KEY, - bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, - TOKEN_SECRET: process.env.TOKEN_SECRET, + googleClientID: process.env.GOOGLE_CLIENT_ID, + googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, + mongoURI: process.env.MONGO_URI, + cookieKey: process.env.COOKIE_KEY, + bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, + TOKEN_SECRET: process.env.TOKEN_SECRET, }; module.exports = keys; \ No newline at end of file From 60753477e0d89ce347f44c49b02cb22e5d9aa576 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 10 Oct 2023 11:18:00 +0200 Subject: [PATCH 061/129] model fix --- models/Courses.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/Courses.js b/models/Courses.js index a44af5ac..4824e7db 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -15,6 +15,8 @@ const courseSchema = new Schema({ coverImg: component, category: String, published: Boolean, + difficulty: Number, + time: Number, sections: [{ type: Schema.Types.ObjectId, ref: "sections" }], creator: [{ type: Schema.Types.ObjectId, ref: "contentCreator" }] }); From 47a59aedaa4599ba54831fab65f6847aac86abcc Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 11:30:27 +0200 Subject: [PATCH 062/129] Fix of test --- __tests__/routes/userRoutes.spec.js | 9 +++++---- middlewares/requireLogin.js | 16 ++++++++++------ package.json | 9 ++++++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 0a8707af..0e90a315 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -17,7 +17,7 @@ app.use((req, res, next) => { app.use('/api/user', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing -const PORT = 5022; // Choose a port for testing +const PORT = 5023; // Choose a port for testing const server = app.listen(PORT, () => { console.log(`Express server is running on port ${PORT}`); }); @@ -94,7 +94,8 @@ describe('Update User Email Route', () => { .expect(404); }); - afterAll((done) => { - server.close(done); - }); + afterAll(async () => { + server.close(); + await mongoose.connection.close(); + }); }); diff --git a/middlewares/requireLogin.js b/middlewares/requireLogin.js index 69e7d8cd..67a5f7ac 100644 --- a/middlewares/requireLogin.js +++ b/middlewares/requireLogin.js @@ -1,7 +1,11 @@ -module.exports = (req, res, next) => { - if (!req.user) { - return res.status(401).send({ error: "You must be logged in!" }); - } +const { verify } = require('../helpers/token'); - next(); -}; +module.exports = (req, res, next) => { + try { + const claims = verify(req.headers.token ?? ''); + next(); + } catch { + // TODO: add updated error messages + return res.status(401).send({ error: 'You must be logged in!' }); + } +}; \ No newline at end of file diff --git a/package.json b/package.json index 0c59a402..28188a87 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "start": "node index.js", "server": "nodemon index.js", - "test": "jest --runInBand --forceExit", + "test": "jest --runInBand", "dev": "npm run server", "prod": "npm run server", "docker": "docker-compose up -d", @@ -31,6 +31,7 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "ejs": "^3.1.5", + "eslint": "^8.50.0", "express": "^4.17.1", "http-proxy-middleware": "^2.0.3", "jsonwebtoken": "^8.5.1", @@ -42,6 +43,7 @@ "nodemon": "^2.0.6", "passport": "^0.6.0", "passport-google-oauth20": "^2.0.0", + "prettier": "^3.0.3", "supertest": "^6.3.3" }, "devDependencies": { @@ -50,8 +52,9 @@ "@babel/node": "^7.2.2", "@babel/preset-env": "^7.3.4", "babel-jest": "^24.4.0", - "jest": "^29.7.0", - "mongodb-memory-server": "^8.9.3" + "jest": "^29.1.2", + "mongodb-memory-server": "^8.9.3", + "supertest": "^6.3.3" }, "jest": { "verbose": true, From c17f1d4d3592176f4820135f5007abdaa679743f Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 11:51:40 +0200 Subject: [PATCH 063/129] Update userRoutes.spec.js --- __tests__/routes/userRoutes.spec.js | 37 ++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 0e90a315..6fa2ea2b 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -3,17 +3,11 @@ const express = require('express'); const router = require('../../routes/userRoutes'); const connectDb = require('../fixtures/db') const makeFakeUser = require('../fixtures/fakeUser') +const { signAccessToken } = require('../../helpers/token'); const mongoose = require('mongoose'); const app = express(); app.use(express.json()); - -// Mock authentication middleware -app.use((req, res, next) => { - req.user = { id: 'mockUserId', /* Needs this for requireLogin middleware */ }; - next(); - }); - app.use('/api/user', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing @@ -22,11 +16,26 @@ const server = app.listen(PORT, () => { console.log(`Express server is running on port ${PORT}`); }); +// Mocked token secret +const TOKEN_SECRET = 'test'; + +// Mock token secret +jest.mock('../../config/keys', () => { + return { + TOKEN_SECRET + }; +}); + +function generateValidToken() { + const token = signAccessToken({ id: 1 }); // Generate a token with the user ID + return token; + } + const fakeUser = makeFakeUser(); describe('Update User Email Route', () => { - let db; + let token = generateValidToken(); beforeAll(async () => { db = await connectDb(); // Connect to the database @@ -38,9 +47,10 @@ describe('Update User Email Route', () => { const newEmail = 'newemail@example.com'; const response = await request(`http://localhost:${PORT}`) - .put('/api/user/update-email/' + fakeUser._id) - .send({ newEmail }) - .expect(200); + .put('/api/user/update-email/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ newEmail }) + .expect(200); expect(response.body.message).toBe('Email updated successfully'); expect(response.body.user.email).toBe(newEmail); @@ -52,6 +62,7 @@ describe('Update User Email Route', () => { await request(`http://localhost:${PORT}`) .put('/api/user/update-email/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers .send({ newEmail }) .expect(404); }); @@ -59,6 +70,7 @@ describe('Update User Email Route', () => { it('updates user first name successfully', async () => { const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${fakeUser._id}`) + .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) .expect(200); @@ -71,6 +83,7 @@ describe('Update User Email Route', () => { await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) .expect(404); }); @@ -78,6 +91,7 @@ describe('Update User Email Route', () => { it('updates user last name successfully', async () => { const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${fakeUser._id}`) + .set('token', token) // Include the token in the request headers .send({ newLastName: 'NewLastName' }) .expect(200); @@ -90,6 +104,7 @@ describe('Update User Email Route', () => { await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers .send({ newLastName: 'NewLastName' }) .expect(404); }); From f9378d0c5cf23a8df7733615faf8dce3b06e054e Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 11:59:19 +0200 Subject: [PATCH 064/129] Small fixes --- README.md | 9 ----- __tests__/setup/globalConfigMongo.json | 2 +- config/keys.js | 2 +- package.json | 9 +++-- routes/userRoutes.js | 46 +++++++++++++------------- users/index.js | 14 ++++---- users/user.js | 12 +++---- users/userListtest.js | 28 ++++++++-------- 8 files changed, 57 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index fddbf703..a728b8b8 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,6 @@ The dev keys contain the following values: - Navigate into the client-web directory - Run `npm install` again -### Setting up AWS CLI -- Use AWS install guide to install on your OS - - [https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html] -- Get AWS keys (access key ID and secret access key) from Daniel/Jacob -- Run `aws configure` and insert access key and secret key -- Set two other potions to 'none' - - Default region name [None]: - - Default output format [None]: - ### Run app in development mode - Run `npm run dev` to start application - By default the web-client runs on ://localhohst:3000 diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index b2653f71..7af41009 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:57000/"} +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:58035/"} \ No newline at end of file diff --git a/config/keys.js b/config/keys.js index f311a61e..4be626a1 100644 --- a/config/keys.js +++ b/config/keys.js @@ -11,7 +11,7 @@ if (process.env.NODE_ENV === 'production') { const keys = { googleClientID: process.env.GOOGLE_CLIENT_ID, googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, - mongoURI: process.env.MONGO_URI, + mongoURI: process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production' ? process.env.MONGO_URI : process.env.MONGO_URI_TEST, cookieKey: process.env.COOKIE_KEY, bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, TOKEN_SECRET: process.env.TOKEN_SECRET, diff --git a/package.json b/package.json index 28188a87..7e5c5370 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,15 @@ "start": "node index.js", "server": "nodemon index.js", "test": "jest --runInBand", - "dev": "npm run server", - "prod": "npm run server", + "dev": "NODE_ENV=development npm run server", + "staging": "NODE_ENV=staging npm run server", + "prod": "NODE_ENV=production npm run server", "docker": "docker-compose up -d", "docker_down": "docker-compose down", "docker_build": "docker-compose up -d --build", - "docker_clean_db": "docker-compose down && docker volume rm educado-backend_mongodb_data && docker-compose up -d --build mongodb" + "docker_clean_db": "docker-compose down && docker volume rm educado-backend_mongodb_data && docker-compose up -d --build mongodb", + "linting": "eslint --ext .js,.jsx,.ts,.tsx .", + "typechecking": "tsc --noEmit" }, "author": "", "license": "ISC", diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 0f1b5ff5..e7c40c08 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -1,32 +1,32 @@ -const router = require("express").Router(); +const router = require(`express`).Router(); // Models -const { User } = require("../models/User"); +const { User } = require(`../models/User`); // Middlewares -const requireLogin = require("../middlewares/requireLogin"); +const requireLogin = require(`../middlewares/requireLogin`); -router.delete("/delete/:id", requireLogin, async (req, res) => { +router.delete(`/delete/:id`, requireLogin, async (req, res) => { try { const { id } = req.params; const deletedUser = await User.findByIdAndDelete(id); if (!deletedUser) { - return res.status(404).json({ error: "User not found" }); + return res.status(404).json({ error: `User not found` }); } // Send a success response - res.status(200).json({ message: 'User deleted successfully' }); + res.status(200).json({ message: `User deleted successfully` }); } catch (error) { // Handle any errors and send an error response - console.error('Error deleting user:', error); - res.status(500).json({ error: 'An error occurred while deleting the user' }); + console.error(`Error deleting user:`, error); + res.status(500).json({ error: `An error occurred while deleting the user` }); } }); // Update User Email route -router.put("/update-email/:id", requireLogin, async (req, res) => { +router.put(`/update-email/:id`, requireLogin, async (req, res) => { try { const { id } = req.params; const { newEmail } = req.body; @@ -38,18 +38,18 @@ router.put("/update-email/:id", requireLogin, async (req, res) => { ); if (!updatedUser) { - return res.status(404).json({ error: "User not found" }); + return res.status(404).json({ error: `User not found` }); } - res.status(200).json({ message: "Email updated successfully", user: updatedUser }); + res.status(200).json({ message: `Email updated successfully`, user: updatedUser }); } catch (error) { - console.error("Error updating email:", error); - res.status(500).json({ error: "An error occurred while updating the email" }); + console.error(`Error updating email:`, error); + res.status(500).json({ error: `An error occurred while updating the email` }); } }); // Update User first name route -router.put("/update-first-name/:id", requireLogin, async (req, res) => { +router.put(`/update-first-name/:id`, requireLogin, async (req, res) => { try { const { id } = req.params; const { newFirstName } = req.body; @@ -61,18 +61,18 @@ router.put("/update-first-name/:id", requireLogin, async (req, res) => { ); if (!updatedUser) { - return res.status(404).json({ error: "User not found" }); + return res.status(404).json({ error: `User not found` }); } - res.status(200).json({ message: "First name updated successfully", user: updatedUser }); + res.status(200).json({ message: `First name updated successfully`, user: updatedUser }); } catch (error) { - console.error("Error updating first name:", error); - res.status(500).json({ error: "An error occurred while updating the first name" }); + console.error(`Error updating first name:`, error); + res.status(500).json({ error: `An error occurred while updating the first name` }); } }); // Update User last name route -router.put("/update-last-name/:id", requireLogin, async (req, res) => { +router.put(`/update-last-name/:id`, requireLogin, async (req, res) => { try { const { id } = req.params; const { newLastName } = req.body; @@ -84,13 +84,13 @@ router.put("/update-last-name/:id", requireLogin, async (req, res) => { ); if (!updatedUser) { - return res.status(404).json({ error: "User not found" }); + return res.status(404).json({ error: `User not found` }); } - res.status(200).json({ message: "Last name updated successfully", user: updatedUser }); + res.status(200).json({ message: `Last name updated successfully`, user: updatedUser }); } catch (error) { - console.error("Error updating last name:", error); - res.status(500).json({ error: "An error occurred while updating the last name" }); + console.error(`Error updating last name:`, error); + res.status(500).json({ error: `An error occurred while updating the last name` }); } }); diff --git a/users/index.js b/users/index.js index 3992c10d..28e71764 100644 --- a/users/index.js +++ b/users/index.js @@ -1,11 +1,11 @@ -const { User } = require('../models/User') -const Email = require('../helpers/email') -const Password = require('../helpers/password') +const { UserModel } = require('../models/User'); +const Email = require('../helpers/email'); +const Password = require('../helpers/password'); -const makeUserList = require('../users/userList') -const userList = makeUserList(User) +const makeUserList = require('../users/userList'); +const userList = makeUserList(UserModel); -const buildMakeUser = require('../users/user') -const makeUser = buildMakeUser({ Email, Password }) +const buildMakeUser = require('../users/user'); +const makeUser = buildMakeUser({ Email, Password }); module.exports = { makeUser, userList } \ No newline at end of file diff --git a/users/user.js b/users/user.js index 1f464970..f99f7ce4 100644 --- a/users/user.js +++ b/users/user.js @@ -5,15 +5,15 @@ module.exports = function buildMakeUser({ Email, Password }) { password, joinedAt = new Date(), modifiedAt = new Date(), - firstName = "Fake first name", - lastName = "Fake last name" + firstName = `Fake first name`, + lastName = `Fake last name` } = {}) { - if (!Email.isValid(email)) throw new Error("User must have a valid email") + if (!Email.isValid(email)) throw new Error(`User must have a valid email`) - if (!password) throw new Error("User must have a password") - if (!(password.length >= 8)) throw new Error("Password should be atleast 8 characters long") - if (password.search("[A-Z]") == -1) throw new Error("Password must contain a capital letter") + if (!password) throw new Error(`User must have a password`) + if (!(password.length >= 8)) throw new Error(`Password should be atleast 8 characters long`) + if (password.search(`[A-Z]`) == -1) throw new Error(`Password must contain a capital letter`) const encrypted = Password.encrypt(password) diff --git a/users/userListtest.js b/users/userListtest.js index 060c87b7..cf28da2f 100644 --- a/users/userListtest.js +++ b/users/userListtest.js @@ -1,23 +1,21 @@ -const mongoose = require('mongoose'); -const { MongoMemoryServer } = require('mongodb-memory-server'); const connectDb = require('../__tests__/fixtures/db') const makeFakeUser = require('../__tests__/fixtures/fakeUser') const { userList } = require('.') -describe("User List", () => { +describe(`User List`, () => { beforeAll(() => connectDb()) afterEach(async () => await userList.remove({})) - it("successfully adds a user to the db", async () => { + it(`successfully adds a user to the db`, async () => { const fakeUser = makeFakeUser() const addedUser = await userList.add(fakeUser) expect(addedUser).not.toBeNull() }) - it("can remove a user from the db", async () => { + it(`can remove a user from the db`, async () => { const fakeUser = makeFakeUser() const added = await userList.add(fakeUser) const removedCount = await userList.remove(added) @@ -25,7 +23,7 @@ describe("User List", () => { expect(removedCount).toBe(1) }) - it("finds a user by email", async () => { + it(`finds a user by email`, async () => { const fakeUser = makeFakeUser() await userList.add(fakeUser) @@ -36,31 +34,31 @@ describe("User List", () => { expect(found.email).toBe(fakeUser.email) }) - it("can update a user's email", async () => { + it(`can update a user's email`, async () => { const fakeUser = makeFakeUser(); await userList.add(fakeUser); - const updatedUser = await userList.updateEmail(fakeUser.email, "newemail@example.com"); + const updatedUser = await userList.updateEmail(fakeUser.email, `newemail@example.com`); - expect(updatedUser.email).toBe("newemail@example.com"); + expect(updatedUser.email).toBe(`newemail@example.com`); }); - it("can update a user's first name", async () => { + it(`can update a user's first name`, async () => { const fakeUser = makeFakeUser(); await userList.add(fakeUser); - const updatedUser = await userList.updateFirstName(fakeUser.firstName, "New First Name"); + const updatedUser = await userList.updateFirstName(fakeUser.firstName, `New First Name`); - expect(updatedUser.firstName).toBe("New First Name"); + expect(updatedUser.firstName).toBe(`New First Name`); }); - it("can update a user's last name", async () => { + it(`can update a user's last name`, async () => { const fakeUser = makeFakeUser(); await userList.add(fakeUser); - const updatedUser = await userList.updateLastName(fakeUser.lastName, "New Last Name"); + const updatedUser = await userList.updateLastName(fakeUser.lastName, `New Last Name`); - expect(updatedUser.lastName).toBe("New Last Name"); + expect(updatedUser.lastName).toBe(`New Last Name`); }); }) From 441c6a7ec88db1bf06dee8aa12c25988eac1f3dc Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 12:03:09 +0200 Subject: [PATCH 065/129] ESlint fixes --- __tests__/fixtures/db.js | 4 +-- __tests__/fixtures/fakeUser.js | 8 ++--- models/Components.js | 6 ++-- models/ContentCreatorApplication.js | 4 +-- models/User.js | 6 ++-- routes/userRoutes.js | 46 ++++++++++++++--------------- users/index.js | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/__tests__/fixtures/db.js b/__tests__/fixtures/db.js index b911a3ae..1e54fa8f 100644 --- a/__tests__/fixtures/db.js +++ b/__tests__/fixtures/db.js @@ -1,6 +1,6 @@ -const mongoose = require('mongoose') +const mongoose = require('mongoose'); -let connection, db +let connection, db; module.exports = async function connectDb() { connection = diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index ad854788..ec3ba81c 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -3,12 +3,12 @@ const mongoose = require('mongoose'); module.exports = function makeFakeUser() { return { - email: "fake@gmail.com", + email: 'fake@gmail.com', password: encrypt('ABC123456!'), - googleID: "1234567891011", + googleID: '1234567891011', joinedAt: new Date(), modifiedAt: new Date(), - firstName: "Fake first name", - lastName: "Fake last name" + firstName: 'Fake first name', + lastName: 'Fake last name' } } diff --git a/models/Components.js b/models/Components.js index e1a5f126..9bf01017 100644 --- a/models/Components.js +++ b/models/Components.js @@ -1,5 +1,5 @@ -// Mongoose model class for Courses -const mongoose = require("mongoose"); +// Mongoose model class for Courses` +const mongoose = require('mongoose'); const { Schema } = mongoose; // Class description @@ -11,6 +11,6 @@ const componentsSchema = new Schema({ dateUpdated: Date, // If its a text component }); -const ComponentModel = mongoose.model("components", componentsSchema); +const ComponentModel = mongoose.model('components', componentsSchema); module.exports = { ComponentModel } diff --git a/models/ContentCreatorApplication.js b/models/ContentCreatorApplication.js index 684b766a..9d011296 100644 --- a/models/ContentCreatorApplication.js +++ b/models/ContentCreatorApplication.js @@ -1,5 +1,5 @@ // Mongoose model class for User -const mongoose = require("mongoose"); +const mongoose = require('mongoose'); const Schema = mongoose.Schema; // Class description @@ -15,7 +15,7 @@ const ContentCreatorSchema = new Schema({ }); const ContentCreatorApplication = mongoose.model( - "Content-Creator-Application", + 'Content-Creator-Application', ContentCreatorSchema ); diff --git a/models/User.js b/models/User.js index eaddae50..6ac27d3c 100644 --- a/models/User.js +++ b/models/User.js @@ -1,5 +1,5 @@ // Mongoose model class for User -const mongoose = require("mongoose") +const mongoose = require('mongoose') const { Schema } = mongoose; // Class description @@ -55,7 +55,7 @@ const userSchema = new Schema({ */ return /^[0-9a-zA-Z.]+@[a-zA-Z]+.[a-zA-Z]{2,4}/.test(email); }, - message: "Invalid email" + message: 'Invalid email' }, validate: { validator: async function(input) { @@ -75,6 +75,6 @@ const userSchema = new Schema({ modifiedAt: Date }); -const UserModel = mongoose.model("users", userSchema); +const UserModel = mongoose.model('users', userSchema); module.exports.User = UserModel diff --git a/routes/userRoutes.js b/routes/userRoutes.js index e7c40c08..b26de95e 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -1,32 +1,32 @@ -const router = require(`express`).Router(); +const router = require('express').Router(); // Models -const { User } = require(`../models/User`); +const { User } = require('../models/User'); // Middlewares -const requireLogin = require(`../middlewares/requireLogin`); +const requireLogin = require('../middlewares/requireLogin'); -router.delete(`/delete/:id`, requireLogin, async (req, res) => { +router.delete('/delete/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const deletedUser = await User.findByIdAndDelete(id); if (!deletedUser) { - return res.status(404).json({ error: `User not found` }); + return res.status(404).json({ error: 'User not found' }); } // Send a success response - res.status(200).json({ message: `User deleted successfully` }); + res.status(200).json({ message: 'User deleted successfully' }); } catch (error) { // Handle any errors and send an error response - console.error(`Error deleting user:`, error); - res.status(500).json({ error: `An error occurred while deleting the user` }); + console.error('Error deleting user:', error); + res.status(500).json({ error: 'An error occurred while deleting the user' }); } }); // Update User Email route -router.put(`/update-email/:id`, requireLogin, async (req, res) => { +router.put('/update-email/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const { newEmail } = req.body; @@ -38,18 +38,18 @@ router.put(`/update-email/:id`, requireLogin, async (req, res) => { ); if (!updatedUser) { - return res.status(404).json({ error: `User not found` }); + return res.status(404).json({ error: 'User not found' }); } - res.status(200).json({ message: `Email updated successfully`, user: updatedUser }); + res.status(200).json({ message: 'Email updated successfully', user: updatedUser }); } catch (error) { - console.error(`Error updating email:`, error); - res.status(500).json({ error: `An error occurred while updating the email` }); + console.error('Error updating email:', error); + res.status(500).json({ error: 'An error occurred while updating the email' }); } }); // Update User first name route -router.put(`/update-first-name/:id`, requireLogin, async (req, res) => { +router.put('/update-first-name/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const { newFirstName } = req.body; @@ -61,18 +61,18 @@ router.put(`/update-first-name/:id`, requireLogin, async (req, res) => { ); if (!updatedUser) { - return res.status(404).json({ error: `User not found` }); + return res.status(404).json({ error: 'User not found' }); } - res.status(200).json({ message: `First name updated successfully`, user: updatedUser }); + res.status(200).json({ message: 'First name updated successfully', user: updatedUser }); } catch (error) { - console.error(`Error updating first name:`, error); - res.status(500).json({ error: `An error occurred while updating the first name` }); + console.error('Error updating first name:', error); + res.status(500).json({ error: 'An error occurred while updating the first name' }); } }); // Update User last name route -router.put(`/update-last-name/:id`, requireLogin, async (req, res) => { +router.put('/update-last-name/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const { newLastName } = req.body; @@ -84,13 +84,13 @@ router.put(`/update-last-name/:id`, requireLogin, async (req, res) => { ); if (!updatedUser) { - return res.status(404).json({ error: `User not found` }); + return res.status(404).json({ error: 'User not found' }); } - res.status(200).json({ message: `Last name updated successfully`, user: updatedUser }); + res.status(200).json({ message: 'Last name updated successfully', user: updatedUser }); } catch (error) { - console.error(`Error updating last name:`, error); - res.status(500).json({ error: `An error occurred while updating the last name` }); + console.error('Error updating last name:', error); + res.status(500).json({ error: 'An error occurred while updating the last name' }); } }); diff --git a/users/index.js b/users/index.js index 28e71764..2f933033 100644 --- a/users/index.js +++ b/users/index.js @@ -8,4 +8,4 @@ const userList = makeUserList(UserModel); const buildMakeUser = require('../users/user'); const makeUser = buildMakeUser({ Email, Password }); -module.exports = { makeUser, userList } \ No newline at end of file +module.exports = { makeUser, userList }; \ No newline at end of file From a64cf26a80b157b9f7ff169567b10ca22ed14a86 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 12:06:19 +0200 Subject: [PATCH 066/129] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a728b8b8..e3cc5b69 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ The dev keys contain the following values: - Navigate into the client-web directory - Run `npm install` again + ### Run app in development mode - Run `npm run dev` to start application - By default the web-client runs on ://localhohst:3000 From efe9f5bf3187be1a536e23907f7f668f5bf035ab Mon Sep 17 00:00:00 2001 From: Profft Date: Tue, 10 Oct 2023 12:08:47 +0200 Subject: [PATCH 067/129] fix: updated an url to fit format and mobile --- .gitignore | 1 + routes/courseRoutes.js | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0fb3cfdd..0cbf355c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ package-lock.json dev.js config/.env *.idea +.idea # enviroment .env diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 4a0f513f..b4595b6c 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -614,12 +614,11 @@ router.post("/courses/:id/unsubscribe", async (req, res) => { }); // Get users subscriptions -router.get("/user/subscriptions/all", async (req, res) => { +router.get("/users/:id/subscriptions/all", async (req, res) => { try { - const { user_id } = req.query; - + const userId = req.params.id; // Find the user by _id and select the 'subscriptions' field - const user = await User.findById(user_id).select('subscriptions'); + const user = await User.findById(userId).select('subscriptions -_id'); if (!user) { return res.status(404).json({ message: 'User not found' }); From d6e7fe27ee0a43e96768af402c3e5b968d5c56ab Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 12:12:17 +0200 Subject: [PATCH 068/129] Update keys.js --- config/keys.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/keys.js b/config/keys.js index 4be626a1..57584b1e 100644 --- a/config/keys.js +++ b/config/keys.js @@ -9,12 +9,12 @@ if (process.env.NODE_ENV === 'production') { // Access the environment variables const keys = { - googleClientID: process.env.GOOGLE_CLIENT_ID, - googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, - mongoURI: process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production' ? process.env.MONGO_URI : process.env.MONGO_URI_TEST, - cookieKey: process.env.COOKIE_KEY, - bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, - TOKEN_SECRET: process.env.TOKEN_SECRET, + googleClientID: process.env.GOOGLE_CLIENT_ID, + googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, + mongoURI: process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production' ? process.env.MONGO_URI : process.env.MONGO_URI_TEST, + cookieKey: process.env.COOKIE_KEY, + bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, + TOKEN_SECRET: process.env.TOKEN_SECRET, }; module.exports = keys; \ No newline at end of file From 7791ef08fa464033388fe0cf15c64e73b1989487 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 12:12:44 +0200 Subject: [PATCH 069/129] Update keys.js --- config/keys.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/keys.js b/config/keys.js index 57584b1e..0cfc2054 100644 --- a/config/keys.js +++ b/config/keys.js @@ -9,12 +9,12 @@ if (process.env.NODE_ENV === 'production') { // Access the environment variables const keys = { - googleClientID: process.env.GOOGLE_CLIENT_ID, - googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, - mongoURI: process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production' ? process.env.MONGO_URI : process.env.MONGO_URI_TEST, - cookieKey: process.env.COOKIE_KEY, - bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, - TOKEN_SECRET: process.env.TOKEN_SECRET, + googleClientID: process.env.GOOGLE_CLIENT_ID, + googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, + mongoURI: process.env.NODE_ENV === 'staging' || process.env.NODE_ENV === 'production' ? process.env.MONGO_URI : process.env.MONGO_URI_TEST, + cookieKey: process.env.COOKIE_KEY, + bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, + TOKEN_SECRET: process.env.TOKEN_SECRET, }; module.exports = keys; \ No newline at end of file From f261b9aa32510bb4cadd03b3039d39c43f91fe43 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Tue, 10 Oct 2023 12:13:11 +0200 Subject: [PATCH 070/129] fix --- config/keys.js | 2 +- routes/courseRoutes.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/keys.js b/config/keys.js index fb22cc20..586fa50b 100644 --- a/config/keys.js +++ b/config/keys.js @@ -11,7 +11,7 @@ if (process.env.NODE_ENV === "production") { const keys = { googleClientID: process.env.GOOGLE_CLIENT_ID, googleClientSecret: process.env.GOOGLE_CLIENT_SECRET, - mongoURI: process.env.MONGO_URI_TEST, + mongoURI: process.env.MONGO_URI, cookieKey: process.env.COOKIE_KEY, bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, TOKEN_SECRET: process.env.TOKEN_SECRET, diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index bce074a9..1ae8526c 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -479,7 +479,7 @@ router.post("/user/", async (req, res) => { //Get all courses -router.get("/courses/all", async (req, res) => { +router.get("/courses", async (req, res) => { try { // find all courses in the database @@ -513,7 +513,7 @@ router.get("/courses/:id", async (req, res) => { // Get all sections from course -router.get("/courses/:id/sections/all", async (req, res) => { +router.get("/courses/:id/sections", async (req, res) => { try { const { id } = req.params; @@ -548,7 +548,7 @@ router.get("/courses/:courseId/sections/:sectionId", async (req, res) => { }); // Get all excercies from a section -router.get("/courses/:courseId/sections/:sectionId/exercises/all", async (req, res) => { +router.get("/courses/:courseId/sections/:sectionId/exercises", async (req, res) => { try { const { courseId, sectionId } = req.params; @@ -614,7 +614,7 @@ router.post("/courses/:id/unsubscribe", async (req, res) => { }); // Get users subscriptions -router.get("/users/:id/subscriptions/all", async (req, res) => { +router.get("/users/:id/subscriptions", async (req, res) => { try { const userId = req.params.id; // Find the user by _id and select the 'subscriptions' field From 39675449e19bb8a8202a26732f4e116f949694dc Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 12:15:53 +0200 Subject: [PATCH 071/129] Use of patterns --- models/User.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/models/User.js b/models/User.js index 6ac27d3c..bb17aa3b 100644 --- a/models/User.js +++ b/models/User.js @@ -1,6 +1,7 @@ // Mongoose model class for User const mongoose = require('mongoose') const { Schema } = mongoose; +const patterns = require('../helpers/patterns'); // Class description const userSchema = new Schema({ @@ -17,7 +18,7 @@ const userSchema = new Schema({ * a space, hyphen or apostrophe, repeated any number of times, * and ending with a sequence of any letters (at least one name). */ - return /^(\p{L}+[ -'])*\p{L}+$/u.test(firstName); + return patterns.name.test(firstName); }, message: 'Invalid first name' } @@ -35,7 +36,7 @@ const userSchema = new Schema({ * a space, hyphen or apostrophe, repeated any number of times, * and ending with a sequence of any letters (at least one name). */ - return /^(\p{L}+[ -'])*\p{L}+$/u.test(lastName); + return patterns.name.test(lastName); }, message: 'Invalid last name' } @@ -53,7 +54,7 @@ const userSchema = new Schema({ * followed by a dot, followed by a sequence of two to four domain * extension letters. */ - return /^[0-9a-zA-Z.]+@[a-zA-Z]+.[a-zA-Z]{2,4}/.test(email); + return patterns.email.test(email); }, message: 'Invalid email' }, From 0993259bec9e2ad418ac938d373c9a241fd49378 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 12:40:11 +0200 Subject: [PATCH 072/129] Update User.js --- models/User.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/User.js b/models/User.js index bb17aa3b..836c74b8 100644 --- a/models/User.js +++ b/models/User.js @@ -18,7 +18,7 @@ const userSchema = new Schema({ * a space, hyphen or apostrophe, repeated any number of times, * and ending with a sequence of any letters (at least one name). */ - return patterns.name.test(firstName); + return /^(\p{L}+[ -'])*\p{L}+$/u.test(firstName); }, message: 'Invalid first name' } @@ -36,7 +36,7 @@ const userSchema = new Schema({ * a space, hyphen or apostrophe, repeated any number of times, * and ending with a sequence of any letters (at least one name). */ - return patterns.name.test(lastName); + return /^(\p{L}+[ -'])*\p{L}+$/u.test(lastName); }, message: 'Invalid last name' } From 83093bd6d89818104a586b1f3c6d3272dd68fbab Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 16:05:59 +0200 Subject: [PATCH 073/129] Added ErroCodes functionality Created a new file called validation which has the functions validateEmail and validateName --- __tests__/routes/signupRoutes.spec.js | 3 +- __tests__/routes/userRoutes.spec.js | 77 +++++++++++++----- helpers/errorCodes.js | 6 +- helpers/validation.js | 63 ++++++++++++++ routes/signupRoutes.js | 54 +----------- routes/userRoutes.js | 113 ++++++++++++++++++-------- 6 files changed, 203 insertions(+), 113 deletions(-) create mode 100644 helpers/validation.js diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 65c27227..0b68fa95 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -78,7 +78,7 @@ describe('Signup User route', () => { .post('/api/signup/user') .send(input) .expect(400); - + expect(response.body.error.code).toBe('E0212'); }); @@ -198,7 +198,6 @@ describe('Signup User route', () => { .expect(400); expect(response.body.error.code).toBe('E0201'); - }); afterAll(async () => { diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 6fa2ea2b..8aa3d165 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -38,11 +38,21 @@ describe('Update User Email Route', () => { let token = generateValidToken(); beforeAll(async () => { - db = await connectDb(); // Connect to the database + db = await connectDb(); // Connect to the database + }); + beforeEach(async () => { + // Insert the fake user into the database before each test await db.collection('users').insertOne(fakeUser); + // Generate a valid token for each test + token = signAccessToken({ id: 1 }); }); + afterEach(async () => { + // Remove the user from the database after each test + await db.collection('users').deleteOne({ _id: fakeUser._id }); + }); + it('updates user email successfully', async () => { const newEmail = 'newemail@example.com'; @@ -52,64 +62,87 @@ describe('Update User Email Route', () => { .send({ newEmail }) .expect(200); - expect(response.body.message).toBe('Email updated successfully'); - expect(response.body.user.email).toBe(newEmail); + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ email: newEmail }); + expect(user).toBeDefined(); + expect(user.email).toBe(newEmail); + }); + + it('Test that emails must be unique when updating', async () => { + const newEmail = fakeUser.email; + + const response = await request(`http://localhost:${PORT}`) + .put('/api/user/update-email/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ newEmail }) + .expect(400); + + expect(response.body.error.code).toBe('E0201'); }); it('handles user not found error for update-email', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; - await request(`http://localhost:${PORT}`) - .put('/api/user/update-email/' + nonExistentUserId) - .set('token', token) // Include the token in the request headers - .send({ newEmail }) - .expect(404); + const response = await request(`http://localhost:${PORT}`) + .put('/api/user/update-email/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers + .send({ newEmail }) + .expect(204); }); it('updates user first name successfully', async () => { + const newFirstName = 'NewFirstName'; + const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${fakeUser._id}`) .set('token', token) // Include the token in the request headers - .send({ newFirstName: 'NewFirstName' }) + .send({ newFirstName: newFirstName }) .expect(200); - expect(response.body.message).toBe('First name updated successfully'); - expect(response.body.user.firstName).toBe('NewFirstName'); + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ firstName: newFirstName }); + expect(user).toBeDefined(); + expect(user.firstName).toBe(newFirstName); }); it('handles user not found error for update-first-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); - await request(`http://localhost:${PORT}`) + const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) - .expect(404); + .expect(204); }); it('updates user last name successfully', async () => { + const newLastName = 'NewLastName'; + const response = await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${fakeUser._id}`) .set('token', token) // Include the token in the request headers - .send({ newLastName: 'NewLastName' }) + .send({ newLastName: newLastName }) .expect(200); - expect(response.body.message).toBe('Last name updated successfully'); - expect(response.body.user.lastName).toBe('NewLastName'); + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ firstName: newLastName }); + expect(user).toBeDefined(); + expect(user.firstName).toBe(newLastName); }); it('handles user not found error for update-last-name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); + const nonExistentUserId = new mongoose.Types.ObjectId(); - await request(`http://localhost:${PORT}`) - .put(`/api/user/update-last-name/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .send({ newLastName: 'NewLastName' }) - .expect(404); + const response = await request(`http://localhost:${PORT}`) + .put(`/api/user/update-last-name/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .send({ newLastName: 'NewLastName' }) + .expect(204); }); afterAll(async () => { + db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection server.close(); await mongoose.connection.close(); }); diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index 7227cc66..8ca6484c 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -17,6 +17,10 @@ module.exports = { code: 'E0003', message: 'Server could not be reached' }, + E0004: { + code: 'E0004', + message: 'User not found' + }, // E01 - Login errors E0101: { @@ -33,7 +37,7 @@ module.exports = { }, E0104: { code: 'E0104', - message: 'Account is suspended or blocked by and administrator.' + message: 'Account is suspended or blocked by an administrator.' }, E0105: { code: 'E0105', diff --git a/helpers/validation.js b/helpers/validation.js new file mode 100644 index 00000000..2f9d4f3e --- /dev/null +++ b/helpers/validation.js @@ -0,0 +1,63 @@ +const { patterns } = require('../helpers/patterns'); +const { User } = require('../models/User'); +const errorCodes = require('../helpers/errorCodes'); + +async function validateEmail(input) { + const emailPattern = patterns.email; + + if (isMissing(input)) { + throw errorCodes['E0208']; // Email is required + } + if (input.length < 6) { + throw errorCodes['E0207']; // Email must be at least 6 characters + } + if (!input.includes('@') || !input.includes('.')) { + throw errorCodes['E0206']; // Email must contain "@" and "." + } + /** + * Email must contain a sequence of any letters, numbers or dots + * followed by an @ symbol, followed by a sequence of any letters + * followed by a dot, followed by a sequence of two to four domain + * extension letters. + */ + + if (await User.findOne({email: input}) != null) { + throw errorCodes['E0201']; // User with the provided email already exists + } + if (!(emailPattern.test(input))) { + throw errorCodes['E0203']; // Invalid email format + } + + return true; +} + +function validateName(input) { + if (isMissing(input)) { + throw errorCodes['E0209']; // First and last name are required + } + if (input.length < 1 || input.length > 50) { + throw errorCodes['E0210']; // Names must be between 1 and 50 characters + } + + /** + * Name can contain a sequence of any letters (including foreign + * language letters such as รฑ, ะ”, and ็›˜) followed by + * a space, hyphen or apostrophe, repeated any number of times, + * and ending with a sequence of any letters (at least one name). + */ + if(!(input.match(/^(\p{L}+[ -'])*\p{L}+$/u))){ + throw errorCodes['E0211']; // Name must only contain letters, spaces, hyphens and apostrophes. + } + + return true; +} + +function isMissing(input) { + return input === undefined || input === null || input === ''; +} + +module.exports = { + validateEmail, + validateName, + isMissing +}; diff --git a/routes/signupRoutes.js b/routes/signupRoutes.js index e3bfab5e..49402a1a 100644 --- a/routes/signupRoutes.js +++ b/routes/signupRoutes.js @@ -1,6 +1,6 @@ const router = require('express').Router(); const { encrypt } = require('../helpers/password'); -const { patterns } = require('../helpers/patterns'); +const { validateEmail, validateName } = require('../helpers/validation'); const { ContentCreatorApplication } = require('../models/ContentCreatorApplication'); const {User} = require('../models/User'); const errorCodes = require('../helpers/errorCodes'); @@ -60,58 +60,6 @@ router.post('/user', async (req, res) => { module.exports = router; -// Might have to make this function async if nothing works -async function validateEmail(input) { - const emailPattern = patterns.email; - - if (isMissing(input)) { - throw errorCodes['E0208']; // Email is required - } - if (input.length < 6) { - throw errorCodes['E0207']; // Email must be at least 6 characters - } - if (!input.includes('@') || !input.includes('.')) { - throw errorCodes['E0206']; // Email must contain "@" and "." - } - /** - * Email must contain a sequence of any letters, numbers or dots - * followed by an @ symbol, followed by a sequence of any letters - * followed by a dot, followed by a sequence of two to four domain - * extension letters. - */ - - if (await User.findOne({email: input}) != null) { - throw errorCodes['E0201']; // User with the provided email already exists - } - if (!(emailPattern.test(input))) { - throw errorCodes['E0203']; // Invalid email format - } - - return true; -} - - -function validateName(input) { - if (isMissing(input)) { - throw errorCodes['E0209']; // First and last name are required - } - if (input.length < 1 || input.length > 50) { - throw errorCodes['E0210']; // Names must be between 1 and 50 characters - } - - /** - * Name can contain a sequence of any letters (including foreign - * language letters such as รฑ, ะ”, and ็›˜) followed by - * a space, hyphen or apostrophe, repeated any number of times, - * and ending with a sequence of any letters (at least one name). - */ - if(!(input.match(/^(\p{L}+[ -'])*\p{L}+$/u))){ - throw errorCodes['E0211']; // Name must only contain letters, spaces, hyphens and apostrophes. - } - - return true; -} - function isMissing(input) { return input === undefined || input === null || input === ''; } \ No newline at end of file diff --git a/routes/userRoutes.js b/routes/userRoutes.js index b26de95e..3d22c68a 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -1,9 +1,7 @@ const router = require('express').Router(); - -// Models +const { validateEmail, validateName } = require('../helpers/validation'); +const errorCodes = require('../helpers/errorCodes'); const { User } = require('../models/User'); - -// Middlewares const requireLogin = require('../middlewares/requireLogin'); router.delete('/delete/:id', requireLogin, async (req, res) => { @@ -13,7 +11,7 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { const deletedUser = await User.findByIdAndDelete(id); if (!deletedUser) { - return res.status(404).json({ error: 'User not found' }); + return res.status(204).json({ error: 'User not found' }); } // Send a success response @@ -31,20 +29,35 @@ router.put('/update-email/:id', requireLogin, async (req, res) => { const { id } = req.params; const { newEmail } = req.body; - const updatedUser = await User.findByIdAndUpdate( - id, - { email: newEmail }, - { new: true } // This ensures that the updated user document is returned - ); + const emailValid = await validateEmail(newEmail); + + if (emailValid) { + const updatedUser = await User.findByIdAndUpdate( + id, + { email: newEmail }, + { new: true } // This ensures that the updated user document is returned + ); - if (!updatedUser) { - return res.status(404).json({ error: 'User not found' }); + if (!updatedUser) { + throw errorCodes['E0004']; + } + + res.status(200); + res.send(updatedUser) } - res.status(200).json({ message: 'Email updated successfully', user: updatedUser }); } catch (error) { - console.error('Error updating email:', error); - res.status(500).json({ error: 'An error occurred while updating the email' }); + if (error === errorCodes['E0004']) { + // Handle "user not found" error response here + res.status(204); + } else { + res.status(400); + } + + console.log(error); + res.send({ + error: error + }); } }); @@ -54,20 +67,35 @@ router.put('/update-first-name/:id', requireLogin, async (req, res) => { const { id } = req.params; const { newFirstName } = req.body; - const updatedUser = await User.findByIdAndUpdate( - id, - { firstName: newFirstName }, - { new: true } // This ensures that the updated user document is returned - ); + const nameValid = await validateName(newFirstName); + + if (nameValid) { + const updatedUser = await User.findByIdAndUpdate( + id, + { firstName: newFirstName }, + { new: true } // This ensures that the updated user document is returned + ); - if (!updatedUser) { - return res.status(404).json({ error: 'User not found' }); + if (!updatedUser) { + throw errorCodes['E0004']; + } + + res.status(200); + res.send(updatedUser) } - res.status(200).json({ message: 'First name updated successfully', user: updatedUser }); } catch (error) { - console.error('Error updating first name:', error); - res.status(500).json({ error: 'An error occurred while updating the first name' }); + if (error === errorCodes['E0004']) { + // Handle "user not found" error response here + res.status(204); + } else { + res.status(400); + } + + console.log(error); + res.send({ + error: error + }); } }); @@ -77,20 +105,35 @@ router.put('/update-last-name/:id', requireLogin, async (req, res) => { const { id } = req.params; const { newLastName } = req.body; - const updatedUser = await User.findByIdAndUpdate( - id, - { lastName: newLastName }, - { new: true } // This ensures that the updated user document is returned - ); + const nameValid = await validateName(newLastName); + + if (nameValid) { + const updatedUser = await User.findByIdAndUpdate( + id, + { firstName: newLastName }, + { new: true } // This ensures that the updated user document is returned + ); - if (!updatedUser) { - return res.status(404).json({ error: 'User not found' }); + if (!updatedUser) { + throw errorCodes['E0004']; + } + + res.status(200); + res.send(updatedUser) } - res.status(200).json({ message: 'Last name updated successfully', user: updatedUser }); } catch (error) { - console.error('Error updating last name:', error); - res.status(500).json({ error: 'An error occurred while updating the last name' }); + if (error === errorCodes['E0004']) { + // Handle "user not found" error response here + res.status(204); + } else { + res.status(400); + } + + console.log(error); + res.send({ + error: error + }); } }); From 640741d6ef40ce794cc1d631339b45dbe26a237e Mon Sep 17 00:00:00 2001 From: Mafusn Date: Tue, 10 Oct 2023 16:07:52 +0200 Subject: [PATCH 074/129] Update userList.js --- users/userList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/userList.js b/users/userList.js index 02754cb0..9c2d4466 100644 --- a/users/userList.js +++ b/users/userList.js @@ -14,7 +14,7 @@ module.exports = function makeUserList(db_model) { return await db_model.create(user); } - async function remove(user = {}) { + async function remove(user) { const results = await db_model.deleteMany(user); return results.deletedCount; } From e6b9478fd8cb9c5817dcee6c0598f45735858a72 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Tue, 10 Oct 2023 16:11:52 +0200 Subject: [PATCH 075/129] feat(api): added GET routes for courses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes made: โ€ขย Added a route for fetching courses made by a specific user โ€ข Reworked route to get all courses โ€ขย Reworked require login to ensure that user specific information can only be accessed by the user in question โ€ข Added adminOnly middleware to check if user is an admin (this is a placeholder without any actual functionality for now) โ€ขย Added tests for the course routes โ€ขย Added _id to fake user (for tests) โ€ข Added fakeCourses file for getting some mock courses for testing purposes --- __tests__/fixtures/fakeCourses.js | 76 +++++++++++++++ __tests__/fixtures/fakeUser.js | 1 + __tests__/routes/authRoutes.spec.js | 1 + __tests__/routes/courseRoutes.test.js | 132 ++++++++++++++++++++++++++ middlewares/adminOnly.js | 17 ++++ middlewares/requireLogin.js | 6 ++ models/Courses.js | 45 ++++++++- routes/courseRoutes.js | 76 ++++++--------- 8 files changed, 300 insertions(+), 54 deletions(-) create mode 100644 __tests__/fixtures/fakeCourses.js create mode 100644 __tests__/routes/courseRoutes.test.js create mode 100644 middlewares/adminOnly.js diff --git a/__tests__/fixtures/fakeCourses.js b/__tests__/fixtures/fakeCourses.js new file mode 100644 index 00000000..feb95af8 --- /dev/null +++ b/__tests__/fixtures/fakeCourses.js @@ -0,0 +1,76 @@ +const courses = [ + { + "title": 'fakeCourse1', + "description": 'fakeCourse1 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "coverImg": 'fakeCourse1 coverImg', + "category": 'mathematics', + "published": false, + "sections": [], + "creator": [], + "difficulty": 1, + "time": 1, + "rating": 5, + }, + { + "title": 'fakeCourse2', + "description": 'fakeCourse2 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "coverImg": 'fakeCourse2 coverImg', + "category": 'language', + "published": false, + "sections": [], + "creator": ['1234567891011'], + "difficulty": 1, + "time": 1, + "rating": 5, + }, + { + "title": 'fakeCourse3', + "description": 'fakeCourse3 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "coverImg": 'fakeCourse3 coverImg', + "category": 'programming', + "published": true, + "sections": [], + "creator": [], + "difficulty": 2, + "time": 3, + "rating": 5, + }, + { + "title": 'fakeCourse4', + "description": 'fakeCourse4 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "coverImg": 'fakeCourse4 coverImg', + "category": 'music', + "published": true, + "sections": [], + "creator": ['1234567891011'], + "difficulty": 6, + "time": 2, + "rating": 3, + } +]; + +/** + * @returns {Array} Array of fake courses + */ +function getFakeCourses() { + return courses; +} + +/** + * @param {Number} creatorId + * @returns {Array} Array of fake courses created by the creator with the given id + */ +function getFakeCoursesByCreator(creatorId) { + const res = courses.filter(course => course.creator.includes(creatorId)); + return res; +} + +module.exports = { getFakeCourses, getFakeCoursesByCreator }; \ No newline at end of file diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 84b0ac99..4bcba154 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -3,6 +3,7 @@ const { encrypt } = require('../../helpers/password'); module.exports = function makeFakeUser() { return { + _id: '1234567891011', email: 'fake@gmail.com', password: encrypt('ABC123456!'), googleID: '1234567891011', diff --git a/__tests__/routes/authRoutes.spec.js b/__tests__/routes/authRoutes.spec.js index f5d577da..6928a408 100644 --- a/__tests__/routes/authRoutes.spec.js +++ b/__tests__/routes/authRoutes.spec.js @@ -36,6 +36,7 @@ describe('Login User route', () => { // Insert the fake user into the database await db.collection('users').insertOne(fakeUser); }); + it('Returns error if user is not found', async () => { const nonExistingUser = { email: 'iDontExist@test.dk', diff --git a/__tests__/routes/courseRoutes.test.js b/__tests__/routes/courseRoutes.test.js new file mode 100644 index 00000000..b6aa527b --- /dev/null +++ b/__tests__/routes/courseRoutes.test.js @@ -0,0 +1,132 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../../routes/courseRoutes'); // Import your router file here +const connectDb = require('../../__tests__/fixtures/db'); +const makeFakeUser = require('../../__tests__/fixtures/fakeUser'); +const { getFakeCourses, getFakeCoursesByCreator } = require('../../__tests__/fixtures/fakeCourses'); +const mongoose = require('mongoose'); +const { encrypt } = require('../../helpers/password'); + +const app = express(); +app.use(express.json()); +app.use('/api', router); // Add your router to the Express app + +// Mock Google OAuth2 clientID +jest.mock('../../config/keys', () => { + return { + GOOGLE_CLIENT_ID: 'test', + TOKEN_SECRET: 'test', + }; +}); + +// Start the Express app on a specific port for testing +const PORT = 5022; // Choose a port for testing +const server = app.listen(PORT, () => { + console.log(`Express server is running on port ${PORT}`); +}); + +const fakeUser = makeFakeUser(); +const fakeCourses = getFakeCourses(); + +describe('Get all courses for user route', () => { + + let db; // Store the database connection + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + // Insert the fake user into the database + await db.collection('courses').insertMany(fakeCourses); + }); + + it('Returns courses made by a given user', async () => { + + const courses = getFakeCoursesByCreator(fakeUser._id); + // Send a get request to the courses endpoint + const res = await request(`http://localhost:${PORT}`) + .get(`/api/courses/creator/${fakeUser._id}`); + // Verify response body + const result = res.body; + + let i = 0; + // Verify that the response body contains the correct data + if (result.length > 0) { + res.body.forEach(course => { + expect(course).toMatchObject({ + _id: expect.any(String), + title: courses[i].title, + description: courses[i].description, + dateCreated: expect.any(String), + dateUpdated: expect.any(String), + coverImg: courses[i].coverImg, + category: courses[i].category, + published: courses[i].published, + sections: courses[i].sections, + creator: courses[i].creator, + difficulty: courses[i].difficulty, + time: courses[i].time, + rating: courses[i].rating, + }); + i++; + }); + } else { + expect(result).toStrictEqual(courses) + } + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + }); + +}); + +describe('Get all courses route', () => { + + let db; // Store the database connection + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + // Insert the fake user into the database + await db.collection('courses').insertMany(fakeCourses); + }); + + it('Returns courses', async () => { + + // Send a get request to the courses endpoint + + const res = await request(`http://localhost:${PORT}`) + .get('/api/courses'); + let i = 0; + res.body.forEach(course => { + expect(course).toMatchObject({ + _id: expect.any(String), + title: fakeCourses[i].title, + description: fakeCourses[i].description, + dateCreated: expect.any(String), + dateUpdated: expect.any(String), + coverImg: fakeCourses[i].coverImg, + category: fakeCourses[i].category, + published: fakeCourses[i].published, + sections: fakeCourses[i].sections, + creator: fakeCourses[i].creator, + difficulty: fakeCourses[i].difficulty, + time: fakeCourses[i].time, + rating: fakeCourses[i].rating, + }); + i++; + }); + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + }); +}); + + +afterAll(async () => { + server.close(); + await mongoose.connection.close(); +}); \ No newline at end of file diff --git a/middlewares/adminOnly.js b/middlewares/adminOnly.js new file mode 100644 index 00000000..8d3eca80 --- /dev/null +++ b/middlewares/adminOnly.js @@ -0,0 +1,17 @@ +const { verify } = require('../helpers/token'); + +const ADMIN_ID = 'srdfet784y2uioejqr' + +module.exports = (req, res, next) => { + console.log("Params: ", req.params); + try { + const claims = verify(req.headers.token ?? ''); + if(claims.id !== ADMIN_ID) { + return res.status(401).send({ error: 'You are not allowed to access this content!' }); + } + next(); + } catch { + // TODO: add updated error messages + return res.status(401).send({ error: 'You must be logged in to acces this content!' }); + } +}; \ No newline at end of file diff --git a/middlewares/requireLogin.js b/middlewares/requireLogin.js index 67a5f7ac..f3f5c261 100644 --- a/middlewares/requireLogin.js +++ b/middlewares/requireLogin.js @@ -1,8 +1,14 @@ const { verify } = require('../helpers/token'); module.exports = (req, res, next) => { + console.log("Params: ", req.params); try { const claims = verify(req.headers.token ?? ''); + if(req.params.id != null) { + if(claims.id !== req.params.id) { + return res.status(401).send({ error: 'You are not allowed to access this content!' }); + } + } next(); } catch { // TODO: add updated error messages diff --git a/models/Courses.js b/models/Courses.js index 95ce10c1..28904ae6 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -7,15 +7,50 @@ const { Schema } = mongoose; // Class description const courseSchema = new Schema({ - title: String, - description: String, - _user: { type: Schema.Types.ObjectId, ref: 'User' }, + title: { + type: String, + required: [true, 'Title is required'], + }, + description: { + type: String, + required: [true, 'Description is required'], + }, dateCreated: Date, dateUpdated: Date, coverImg: String, - category: String, + category: { + type: String, + enum: ['Math', 'Science', 'Finance', 'Language', 'Sustainability', 'Other'], + }, published: Boolean, - sections: [{ type: Schema.Types.ObjectId, ref: 'Component' }], + sections: [{ + type: Schema.Types.ObjectId, + ref: 'Component' + }], + creator: { + type: Schema.Types.ObjectId, + ref: 'User' + }, + difficulty: { + Number, + Range: [1, 5], + default: 3, + }, + status: { + type: String, + enum: ['draft', 'published', 'hidden'], + default: 'draft', + }, + estimatedHours: Number, + rating: { + Number, + Range: [0.0, 5.0], + default: 0.0, + }, + numOfSubscriptions:{ + type: Number, + default: 0, + }, }); const CourseModel = mongoose.model('courses', courseSchema); // Create new collection called courses, using the courseScema diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 2206e405..742e0aba 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -4,35 +4,11 @@ const router = require('express').Router(); const { CourseModel } = require('../models/Courses'); const { SectionModel } = require('../models/Sections'); const { ComponentModel } = require('../models/Components'); -const { - ContentCreatorApplication, -} = require('../models/ContentCreatorApplication'); +const { ContentCreatorApplication } = require('../models/ContentCreatorApplication'); const requireLogin = require('../middlewares/requireLogin'); +const adminOnly = require('../middlewares/adminOnly'); -// Content Creator Application Route -router.post('/course/', async (req, res) => { - const { title, description } = req.body; - - const course = new CourseModel({ - title: title, - description: description, - category: '', - _user: req.user.id, - dateCreated: Date.now(), - dateUpdated: Date.now(), - sections: [], - }); - - try { - await course.save(); - res.send(course); - } catch (err) { - res.status(422).send(err); - } -}); - -// Course routes - +// Create Content Creator Application Route router.post('/courses', async (req, res) => { const { title, description } = req.body; @@ -55,7 +31,7 @@ router.post('/courses', async (req, res) => { }); // Update Course -router.post('/course/update', requireLogin, async (req, res) => { +router.post('/courses/update', requireLogin, async (req, res) => { const { course } = req.body; const dbCourse = await CourseModel.findByIdAndUpdate( course._id, @@ -76,27 +52,29 @@ router.post('/course/update', requireLogin, async (req, res) => { res.send('Course Update Complete'); }); -// Get all courses for user -router.get('/course/getall', async (req, res) => { - const list = await CourseModel.find({ _user: req.user.id }); - res.send(list); -}); - -// Get all courses for user -router.get('/course/eml/getall', async (req, res) => { - const list = await CourseModel.find(); - res.send(list); +// Get all courses +router.get('/courses', adminOnly, async (req, res) => { + const result = await CourseModel.find({}); + res.send(result); }); // FIXME: no error handling, just needed the endpoint - Mvh. Frederik -router.get('/course/:id', async (req, res) => { +router.get('/courses/:id', async (req, res) => { const { id } = req.params; // destructure params const course = await CourseModel.findById(id); res.send(course); }); +// Get all courses for one user +router.get('/courses/creator/:id', requireLogin, async (req, res) => { + const id = req.params.id; + const courses = await CourseModel.find({creator: id}); // Find courses for a specific user + + res.send(courses); // Send response +}); + // Update course title -router.post('/course/update/title', async (req, res) => { +router.post('/courses/update/title', async (req, res) => { const { text, course_id } = req.body; // find object in database and update title to new value @@ -109,7 +87,7 @@ router.post('/course/update/title', async (req, res) => { }); // Update course description -router.post('/course/update/description', async (req, res) => { +router.post('/courses/update/description', async (req, res) => { const { text, course_id } = req.body; // find object in database and update title to new value @@ -126,7 +104,7 @@ router.post('/course/update/description', async (req, res) => { }); // Update course category -router.post('/course/update/category', async (req, res) => { +router.post('/courses/update/category', async (req, res) => { const { text, course_id } = req.body; // find object in database and update title to new value @@ -139,7 +117,7 @@ router.post('/course/update/category', async (req, res) => { }); // Update course published state -router.post('/course/update/published', async (req, res) => { +router.post('/courses/update/published', async (req, res) => { const { published, course_id } = req.body; // find object in database and update title to new value @@ -156,7 +134,7 @@ router.post('/course/update/published', async (req, res) => { }); // Delete all documents for user - the Nueclear option. -router.post('/course/delete', requireLogin, async (req, res) => { +router.post('/courses/delete', requireLogin, async (req, res) => { const { course_id } = req.body; let course; try { @@ -220,7 +198,7 @@ router.post('/section/create', requireLogin, async (req, res) => { }); // Get all sections -router.post('/course/getallsections', requireLogin, async (req, res) => { +router.post('/courses/getallsections', requireLogin, async (req, res) => { const { sections } = req.body; let list = []; for (let i = 0; i < sections.length; i++) { @@ -231,7 +209,7 @@ router.post('/course/getallsections', requireLogin, async (req, res) => { }); // Update section title -router.post('/course/update/sectiontitle', async (req, res) => { +router.post('/courses/update/sectiontitle', async (req, res) => { // ... // get new value & section ID const { value, sectionId } = req.body; @@ -275,7 +253,7 @@ router.post('/section/update/description', async (req, res) => { }); // Update sections order -router.post('/course/update/sectionsorder', async (req, res) => { +router.post('/courses/update/sectionsorder', async (req, res) => { // Get sections from request const { sections, course_id } = req.body; // REPORT NOTE: Mรฅske lav performance test, for om det giver bedst mening at wipe array og overskrive, eller tjekke 1 efter 1 om updates @@ -413,7 +391,7 @@ router.post('/component/delete', requireLogin, async (req, res) => { res.send(componentIds); }); -router.post('/eml/course/getallsections', async (req, res) => { +router.post('/eml/courses/getallsections', async (req, res) => { const { sections } = req.body; let list = []; for (let i = 0; i < sections.length; i++) { @@ -424,7 +402,7 @@ router.post('/eml/course/getallsections', async (req, res) => { }); // Delete all documents for user -router.get('/course/delete_all', requireLogin, async (req, res) => { +router.get('/courses/delete_all', requireLogin, async (req, res) => { await CourseModel.deleteMany({ _user: req.user.id }, (err) => { console.log(err); }); From 476d4465c3d428b8ef0fe93f8f2e27c0e826e4c4 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 11 Oct 2023 09:14:52 +0200 Subject: [PATCH 076/129] Tests for deleteUser created --- __tests__/routes/userRoutes.spec.js | 33 +++++++++++++++++++++++------ routes/userRoutes.js | 23 ++++++++++++++------ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 8aa3d165..c5e49e50 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -53,10 +53,31 @@ describe('Update User Email Route', () => { await db.collection('users').deleteOne({ _id: fakeUser._id }); }); + it('deletes a user successfully', async () => { + // Delete the user using the API + await request(`http://localhost:${PORT}`) + .delete(`/api/user/delete/${fakeUser._id}`) + .set('token', token) // Include the token in the request headers + .expect(200); + + // Verify that the user was deleted from the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeNull(); + }); + + it('handles user not found error for delete', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + + await request(`http://localhost:${PORT}`) + .delete(`/api/user/delete/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .expect(204); + }); + it('updates user email successfully', async () => { const newEmail = 'newemail@example.com'; - const response = await request(`http://localhost:${PORT}`) + await request(`http://localhost:${PORT}`) .put('/api/user/update-email/' + fakeUser._id) .set('token', token) // Include the token in the request headers .send({ newEmail }) @@ -84,7 +105,7 @@ describe('Update User Email Route', () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; - const response = await request(`http://localhost:${PORT}`) + await request(`http://localhost:${PORT}`) .put('/api/user/update-email/' + nonExistentUserId) .set('token', token) // Include the token in the request headers .send({ newEmail }) @@ -94,7 +115,7 @@ describe('Update User Email Route', () => { it('updates user first name successfully', async () => { const newFirstName = 'NewFirstName'; - const response = await request(`http://localhost:${PORT}`) + await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${fakeUser._id}`) .set('token', token) // Include the token in the request headers .send({ newFirstName: newFirstName }) @@ -109,7 +130,7 @@ describe('Update User Email Route', () => { it('handles user not found error for update-first-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); - const response = await request(`http://localhost:${PORT}`) + await request(`http://localhost:${PORT}`) .put(`/api/user/update-first-name/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) @@ -119,7 +140,7 @@ describe('Update User Email Route', () => { it('updates user last name successfully', async () => { const newLastName = 'NewLastName'; - const response = await request(`http://localhost:${PORT}`) + await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${fakeUser._id}`) .set('token', token) // Include the token in the request headers .send({ newLastName: newLastName }) @@ -134,7 +155,7 @@ describe('Update User Email Route', () => { it('handles user not found error for update-last-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); - const response = await request(`http://localhost:${PORT}`) + await request(`http://localhost:${PORT}`) .put(`/api/user/update-last-name/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newLastName: 'NewLastName' }) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 3d22c68a..8dcf7562 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -10,16 +10,27 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { const deletedUser = await User.findByIdAndDelete(id); + console.log(deletedUser) + if (!deletedUser) { - return res.status(204).json({ error: 'User not found' }); + throw errorCodes['E0004']; + } else { + res.status(200); + res.send(deletedUser) } - // Send a success response - res.status(200).json({ message: 'User deleted successfully' }); } catch (error) { - // Handle any errors and send an error response - console.error('Error deleting user:', error); - res.status(500).json({ error: 'An error occurred while deleting the user' }); + if (error === errorCodes['E0004']) { + // Handle "user not found" error response here + res.status(204); + } else { + res.status(400); + } + + console.log(error); + res.send({ + error: error + }); } }); From 5df739b861c230b1edde535401880b15549c2a3d Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 11 Oct 2023 09:21:24 +0200 Subject: [PATCH 077/129] Fix of update last name --- __tests__/routes/userRoutes.spec.js | 4 ++-- routes/userRoutes.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index c5e49e50..4364848d 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -147,9 +147,9 @@ describe('Update User Email Route', () => { .expect(200); // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ firstName: newLastName }); + const user = await db.collection('users').findOne({ lastName: newLastName }); expect(user).toBeDefined(); - expect(user.firstName).toBe(newLastName); + expect(user.lastName).toBe(newLastName); }); it('handles user not found error for update-last-name', async () => { diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 8dcf7562..ed5fc7b7 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -121,7 +121,7 @@ router.put('/update-last-name/:id', requireLogin, async (req, res) => { if (nameValid) { const updatedUser = await User.findByIdAndUpdate( id, - { firstName: newLastName }, + { lastName: newLastName }, { new: true } // This ensures that the updated user document is returned ); From b428e609ab9ee0ee84bd0cbf93d48c004ebcb142 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 11 Oct 2023 09:26:36 +0200 Subject: [PATCH 078/129] test! --- __tests__/fixtures/fakeCourse.js | 13 ++ __tests__/fixtures/fakeSection.js | 14 ++ __tests__/fixtures/fakeUser.js | 5 +- __tests__/routes/courseRoutes.specs.js | 197 ++++++++++++++++++ __tests__/routes/coursesTest.js | 30 --- __tests__/setup/globalConfigMongo.json | 2 +- ...ontentCreatorApplicationControllerspec.js} | 0 ...s => contentCreatorApplicationListspec.js} | 0 models/ContentCreatorApplication.js | 2 +- models/Courses.js | 4 +- models/Exercises.js | 2 +- models/Sections.js | 6 +- models/User.js | 2 +- package.json | 1 - routes/courseRoutes.js | 4 +- 15 files changed, 240 insertions(+), 42 deletions(-) create mode 100644 __tests__/fixtures/fakeCourse.js create mode 100644 __tests__/fixtures/fakeSection.js create mode 100644 __tests__/routes/courseRoutes.specs.js delete mode 100644 __tests__/routes/coursesTest.js rename applications/content-creator-applications/controller/{contentCreatorApplicationController.spec.js => contentCreatorApplicationControllerspec.js} (100%) rename applications/content-creator-applications/data-access/{contentCreatorApplicationList.spec.js => contentCreatorApplicationListspec.js} (100%) diff --git a/__tests__/fixtures/fakeCourse.js b/__tests__/fixtures/fakeCourse.js new file mode 100644 index 00000000..ee8028ea --- /dev/null +++ b/__tests__/fixtures/fakeCourse.js @@ -0,0 +1,13 @@ +module.exports = function makeFakeCourse() { + + return { + sections: [], + title: 'test course', + category: 'test', + difficulty: 1, + hours: 10, + description: "This course is a test course", + dateCreated: new Date(), + dateUpdated: new Date() + }; +}; diff --git a/__tests__/fixtures/fakeSection.js b/__tests__/fixtures/fakeSection.js new file mode 100644 index 00000000..fe74f0e3 --- /dev/null +++ b/__tests__/fixtures/fakeSection.js @@ -0,0 +1,14 @@ +module.exports = function makeFakeSection() { + + return { + exercises: [], + title: 'test section', + description: 'this is a test section', + sectionNumber: 1, + createdAt: Date, + modifiedAt: Date, + totalPoints: 100, + components: [], + parentCourse: '1abc', + }; +}; \ No newline at end of file diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 84b0ac99..538f42d6 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -7,6 +7,9 @@ module.exports = function makeFakeUser() { password: encrypt('ABC123456!'), googleID: '1234567891011', joinedAt: new Date(), - modifiedAt: new Date() + modifiedAt: new Date(), + subscriptions: [ + { _id: 'testSub123'} + ] }; }; diff --git a/__tests__/routes/courseRoutes.specs.js b/__tests__/routes/courseRoutes.specs.js new file mode 100644 index 00000000..8ede94ec --- /dev/null +++ b/__tests__/routes/courseRoutes.specs.js @@ -0,0 +1,197 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../../routes/courseRoutes'); +const mongoose = require('mongoose'); +const connectDb = require('../../__tests__/fixtures/db'); +const makeFakeUser = require('../fixtures/fakeUser'); +const makeFakeCourse = require('../fixtures/fakeCourse'); +const makeFakeSection = require('../fixtures/fakeSection'); +const CourseModel = require('../../models/Courses'); +const SectionModel = require('../../models/Sections'); +const ExerciseModel = require('../../models/Exercises'); +const User = require('../../models/User'); + +const app = express(); +app.use(express.json()); +app.use('/api', router); // Mount the router under '/api' path + +// Start the Express app on a specific port for testing +const PORT = 5021; // Choose a port for testing +const server = app.listen(PORT, () => { + console.log(`Express server is running on port ${PORT}`); +}); + +// Create a fake user, course and section +let fakeUser = makeFakeUser(); +let fakeCourse = makeFakeCourse(); +let fakeSection = makeFakeSection(); + +describe('Course Routes', () => { + + let db; // Store the database connection + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + // Insert the fake user, courses and sections into the database + await db.collection('users').insertOne(fakeUser); + await db.collection('courses').insertOne(fakeCourse); + await db.collection('sections').insertOne(fakeSection); + + }); + + describe('GET /courses', () => { + it('should get all courses', async () => { + + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses'); + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Array); + + }); + }); + + describe('GET /courses/:id', () => { + it('should get a specific course', async () => { + const course = await db.collection('courses').findOne({ title: 'test course'}); + const courseId = course._id + + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId) + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Object); + + }); + }); + + describe('GET /courses/:id/sections', () => { + it('should get all sections from a course', async () => { + const course = await db.collection('courses').findOne({ title: 'test course'}); + const courseId = course._id; + + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections'); + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Array); + + }); + }); + + describe('GET /courses/:courseId/sections/:sectionId', () => { + it('should get a specific section', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course'}); + const courseId = course._id; + + const section = await db.collection('sections').findOne({title: 'test section'}); + const sectionId = section._id; + + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections/' + sectionId); + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Object); + + }); + }); + + describe('POST /courses/:id/subscribe', () => { + it('should subscribe a user to a course', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course'}); + const courseId = course._id; + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const userId = user._id; + + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/subscribe') + .send({ user_id: userId}); + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Object); + + }); + }); + + describe('POST /courses/:id/unsubscribe', () => { + it('should unsubscribe a user from a course', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course'}); + const courseId = course._id; + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const userId = user._id; + + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/unsubscribe') + .send({ user_id: userId }); + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Object); + + }); + }); + + describe('GET /users/:id/subscriptions', () => { + it('should get user subscriptions', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const userId = user._id; + + const response = await request(`http://localhost:${PORT}`) + .get('/api/users/' + userId + '/subscriptions'); + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Array); + + }); + }); + + describe('GET /users', () => { + + it('should check if a user is subscribed to a specific course', async () => { + + const courseId = 'testSub123'; + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const userId = user._id; + + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + + expect(response.status).toBe(200); + expect(response.text).toBe('true'); + + }); + + it('should return false if a user is not subscribed to a specific course', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course'}); + const courseId = course._id; + + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const userId = user._id; + + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + + expect(response.status).toBe(200); + expect(response.text).toBe('false'); + + }); + }); + + +afterAll(async () => { + db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + db.collection('sections').deleteMany({}); // Delete all documents in the 'sections' collection + server.close(); + await mongoose.connection.close(); +}); + +}); diff --git a/__tests__/routes/coursesTest.js b/__tests__/routes/coursesTest.js deleted file mode 100644 index d4bdc3d0..00000000 --- a/__tests__/routes/coursesTest.js +++ /dev/null @@ -1,30 +0,0 @@ -const request = require('supertest'); -const express = require('express'); -const router = require('../../routes/courseRoutes'); // Import your router file here - -const test_courseId = ; - -const app = express(); -app.use(express.json()); -app.use('/api', router); // Mount the router under '/api' path - -describe('Courses Routes', () => { - // Test the GET /courses/all route - it('should get all courses', async () => { - const response = await request(app).get('/courses/all'); - expect(response.status).toBe(200); - expect(response.body).toBeInstanceOf(Array); - // Add more assertions as needed to check the response data - }); - - // Test the GET /courses/:id route - it('should get a specific course', async () => { - const courseId = 'replace-with-an-existing-course-id'; // Replace with an actual course ID - const response = await request(app).get(`/courses/${courseId}`); - expect(response.status).toBe(200); - expect(response.body).toBeInstanceOf(Object); - // Add more assertions as needed to check the response data - }); - - // Add more test cases for other routes as needed -}); diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index b3154bb1..6867fe25 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:51309/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:58693/"} \ No newline at end of file diff --git a/applications/content-creator-applications/controller/contentCreatorApplicationController.spec.js b/applications/content-creator-applications/controller/contentCreatorApplicationControllerspec.js similarity index 100% rename from applications/content-creator-applications/controller/contentCreatorApplicationController.spec.js rename to applications/content-creator-applications/controller/contentCreatorApplicationControllerspec.js diff --git a/applications/content-creator-applications/data-access/contentCreatorApplicationList.spec.js b/applications/content-creator-applications/data-access/contentCreatorApplicationListspec.js similarity index 100% rename from applications/content-creator-applications/data-access/contentCreatorApplicationList.spec.js rename to applications/content-creator-applications/data-access/contentCreatorApplicationListspec.js diff --git a/models/ContentCreatorApplication.js b/models/ContentCreatorApplication.js index eb027bba..e984df68 100644 --- a/models/ContentCreatorApplication.js +++ b/models/ContentCreatorApplication.js @@ -15,7 +15,7 @@ const contentCreatorSchema = new Schema({ }); const ContentCreator = mongoose.model( - "contentCreator", + "contentCreators", contentCreatorSchema ); diff --git a/models/Courses.js b/models/Courses.js index fa13b5fb..35071825 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -18,11 +18,11 @@ const courseSchema = new Schema({ difficulty: Number, time: Number, sections: [{ type: Schema.Types.ObjectId, ref: "sections" }], - creator: [{ type: Schema.Types.ObjectId, ref: "contentCreator" }] + creator: { type: Schema.Types.ObjectId, ref: "contentCreator" } }); const CourseModel = mongoose.model( - "course", + "courses", courseSchema ); diff --git a/models/Exercises.js b/models/Exercises.js index 0494b9b7..e38c8d8f 100644 --- a/models/Exercises.js +++ b/models/Exercises.js @@ -17,7 +17,7 @@ const exerciseSchema = new Schema ({ }) const ExerciseModel = mongoose.model( - "exercise", + "exercises", exerciseSchema ); diff --git a/models/Sections.js b/models/Sections.js index 908aff9d..d29b9ee4 100644 --- a/models/Sections.js +++ b/models/Sections.js @@ -5,15 +5,15 @@ const { Schema } = mongoose; // Class description const sectionSchema = new Schema({ - exercises: [{ type: Schema.Types.ObjectId, ref: "exercise" }], + exercises: [{ type: Schema.Types.ObjectId, ref: "exercises" }], title: String, description: String, sectionNumber: Number, createdAt: Date, modifiedAt: Date, totalPoints: Number, - components: [{ type: Schema.Types.ObjectId, ref: "component" }], - parentCourse: { type: Schema.Types.ObjectId, ref: "Course" }, + components: [{ type: Schema.Types.ObjectId, ref: "components" }], + parentCourse: { type: Schema.Types.ObjectId, ref: "courses" }, }); const SectionModel = mongoose.model('sections', sectionSchema); diff --git a/models/User.js b/models/User.js index 5aba66b0..eb3b8fe8 100644 --- a/models/User.js +++ b/models/User.js @@ -73,7 +73,7 @@ const userSchema = new Schema({ password: String, joinedAt: Date, modifiedAt: Date, - subscriptions: [{ type: Schema.Types.ObjectId, ref: "course" }] + subscriptions: [{ type: Schema.Types.ObjectId, ref: "courses" }] }); const UserModel = mongoose.model('users', userSchema); diff --git a/package.json b/package.json index 9c4011c2..05929f04 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "nodemon": "^2.0.6", "passport": "^0.6.0", "passport-google-oauth20": "^2.0.0", - "supertest": "^6.3.3", "prettier": "^3.0.3" }, "devDependencies": { diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 2e02163a..76f3f7cc 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -547,7 +547,8 @@ router.get("/courses/:courseId/sections/:sectionId", async (req, res) => { }); -// Get all excercies from a section +/* +// Get all excercies from a section *** commented out since we do not use it per 10/10 router.get("/courses/:courseId/sections/:sectionId/exercises", async (req, res) => { try { @@ -563,6 +564,7 @@ router.get("/courses/:courseId/sections/:sectionId/exercises", async (req, res) } }); +*/ /*** SUBSCRIPTION ROUTES ***/ From 2cbcc2dd1615a6d400edda60241a227280e0da37 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Wed, 11 Oct 2023 09:45:31 +0200 Subject: [PATCH 079/129] Fix of duplicate code in userRoutes.spec.js --- __tests__/routes/userRoutes.spec.js | 56 ++++++++++++----------------- routes/userRoutes.js | 2 -- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 4364848d..8cedf4bb 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -26,26 +26,21 @@ jest.mock('../../config/keys', () => { }; }); -function generateValidToken() { - const token = signAccessToken({ id: 1 }); // Generate a token with the user ID - return token; - } - -const fakeUser = makeFakeUser(); describe('Update User Email Route', () => { - let db; - let token = generateValidToken(); + let token, fakeUser, db; beforeAll(async () => { db = await connectDb(); // Connect to the database + + token = signAccessToken({ id: 1 }); + fakeUser = makeFakeUser(); + }); beforeEach(async () => { // Insert the fake user into the database before each test await db.collection('users').insertOne(fakeUser); - // Generate a valid token for each test - token = signAccessToken({ id: 1 }); }); afterEach(async () => { @@ -113,18 +108,9 @@ describe('Update User Email Route', () => { }); it('updates user first name successfully', async () => { - const newFirstName = 'NewFirstName'; - - await request(`http://localhost:${PORT}`) - .put(`/api/user/update-first-name/${fakeUser._id}`) - .set('token', token) // Include the token in the request headers - .send({ newFirstName: newFirstName }) - .expect(200); + const newFirstName = 'newFirstName'; - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ firstName: newFirstName }); - expect(user).toBeDefined(); - expect(user.firstName).toBe(newFirstName); + await updateUserProperty('first-name', newFirstName, newFirstName, 'firstName'); }); it('handles user not found error for update-first-name', async () => { @@ -138,18 +124,9 @@ describe('Update User Email Route', () => { }); it('updates user last name successfully', async () => { - const newLastName = 'NewLastName'; + const newLastName = 'newLastName'; - await request(`http://localhost:${PORT}`) - .put(`/api/user/update-last-name/${fakeUser._id}`) - .set('token', token) // Include the token in the request headers - .send({ newLastName: newLastName }) - .expect(200); - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ lastName: newLastName }); - expect(user).toBeDefined(); - expect(user.lastName).toBe(newLastName); + await updateUserProperty('last-name', newLastName, newLastName, 'lastName'); }); it('handles user not found error for update-last-name', async () => { @@ -167,4 +144,17 @@ describe('Update User Email Route', () => { server.close(); await mongoose.connection.close(); }); -}); + + const updateUserProperty = async (route, propertyName, propertyValue, dbField) => { + await request(`http://localhost:${PORT}`) + .put(`/api/user/update-${route}/${fakeUser._id}`) + .set('token', token) // Include the token in the request headers + .send({ [propertyName]: propertyValue }) + .expect(200); + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ [dbField]: propertyValue }); + expect(user).toBeDefined(); + expect(user[dbField]).toBe(propertyValue); + }; +}); \ No newline at end of file diff --git a/routes/userRoutes.js b/routes/userRoutes.js index ed5fc7b7..9fc6b529 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -10,8 +10,6 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { const deletedUser = await User.findByIdAndDelete(id); - console.log(deletedUser) - if (!deletedUser) { throw errorCodes['E0004']; } else { From 18a43fdcfd654fe923cc3cc85ebbbbfc1c00dbbd Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 11 Oct 2023 09:54:43 +0200 Subject: [PATCH 080/129] Update globalConfigMongo.json --- __tests__/setup/globalConfigMongo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index 6867fe25..de4f9b5e 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:58693/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:59260/"} \ No newline at end of file From bd6c29bff176bf49b5c1bc078cb0680b518422d8 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Wed, 11 Oct 2023 10:46:59 +0200 Subject: [PATCH 081/129] fix(api): Added tests and fixed course model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes made: โ€ขย Added tests for admin only routes โ€ขย Added tests for course routes when unauthorized โ€ขย Removed some invalid fields in courses model --- __tests__/fixtures/fakeUser.js | 4 +- __tests__/middleware/adminOnly.test.js | 68 ++++++++++++++++++++++++++ __tests__/routes/courseRoutes.test.js | 35 ++++++++++++- __tests__/setup/globalConfigMongo.json | 1 - models/Courses.js | 6 +-- 5 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 __tests__/middleware/adminOnly.test.js delete mode 100644 __tests__/setup/globalConfigMongo.json diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 4bcba154..48906b3f 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -1,9 +1,11 @@ +const { Mongoose } = require('mongoose'); const { encrypt } = require('../../helpers/password'); module.exports = function makeFakeUser() { return { - _id: '1234567891011', + // Make a objectid string for mongoose + _id: "5f8f8a8f8f8f8f8f8f8f8f8f", email: 'fake@gmail.com', password: encrypt('ABC123456!'), googleID: '1234567891011', diff --git a/__tests__/middleware/adminOnly.test.js b/__tests__/middleware/adminOnly.test.js new file mode 100644 index 00000000..567675fd --- /dev/null +++ b/__tests__/middleware/adminOnly.test.js @@ -0,0 +1,68 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../../routes/courseRoutes'); // Import your router file here +const connectDb = require('../fixtures/db'); +const { signAccessToken } = require('../../helpers/token'); +const mongoose = require('mongoose'); + +const app = express(); +app.use(express.json()); +app.use('/api', router); // Mount the router under '/api' path + +// Start the Express app on a specific port for testing +const PORT = 5022; // Choose a port for testing +const server = app.listen(PORT, () => { + console.log(`Express server is running on port ${PORT}`); +}); + +// Mocked token secret +const TOKEN_SECRET = 'test'; + +// Mock token secret +jest.mock('../../config/keys', () => { + return { + TOKEN_SECRET + }; +}); + +describe('Admin token verify', () => { + let db; + + beforeAll(done => { + done(); + db = connectDb(); // Connect to the database + }); + + it('Return an error if no valid admin token is present on private route', async () => { + const token = 'ImAnInvalidToken'; + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', token) + .expect(401); + + expect(response.body.error).toBeDefined(); + }); + + it('Return success if an admin token is valid on a private route', async () => { + const token = signAccessToken({ id: 'srdfet784y2uioejqr' }); + + // mock that token is valid + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', token) + .expect(200); + }); + + it('Test for non-algorithm attack', async () => { + const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.' + btoa(`{"id":1,"iat":${'' + Date.now()},"exp":999999999999}`) + '.'; + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', token) + .expect(401); + }); + + afterAll(async () => { + server.close(); + await mongoose.connection.close(); + }); +}); \ No newline at end of file diff --git a/__tests__/routes/courseRoutes.test.js b/__tests__/routes/courseRoutes.test.js index b6aa527b..11372fe9 100644 --- a/__tests__/routes/courseRoutes.test.js +++ b/__tests__/routes/courseRoutes.test.js @@ -6,6 +6,7 @@ const makeFakeUser = require('../../__tests__/fixtures/fakeUser'); const { getFakeCourses, getFakeCoursesByCreator } = require('../../__tests__/fixtures/fakeCourses'); const mongoose = require('mongoose'); const { encrypt } = require('../../helpers/password'); +const { signAccessToken } = require('../../helpers/token'); const app = express(); app.use(express.json()); @@ -21,6 +22,7 @@ jest.mock('../../config/keys', () => { // Start the Express app on a specific port for testing const PORT = 5022; // Choose a port for testing +const ADMIN_ID = 'srdfet784y2uioejqr'; const server = app.listen(PORT, () => { console.log(`Express server is running on port ${PORT}`); }); @@ -44,7 +46,9 @@ describe('Get all courses for user route', () => { const courses = getFakeCoursesByCreator(fakeUser._id); // Send a get request to the courses endpoint const res = await request(`http://localhost:${PORT}`) - .get(`/api/courses/creator/${fakeUser._id}`); + .get(`/api/courses/creator/${fakeUser._id}`) + .set('token', signAccessToken({ id: fakeUser._id })); + expect(res.statusCode).toEqual(200); // Verify response body const result = res.body; @@ -74,6 +78,19 @@ describe('Get all courses for user route', () => { } }); + it('Returns error 401 if user is not authorized to access', async () => { + + // Send a get request to the courses endpoint + const res = await request(`http://localhost:${PORT}`) + .get(`/api/courses/creator/${fakeUser._id}`) + .set('token', signAccessToken({ id: 'notAuthorized' })); + expect(res.statusCode).toEqual(401); + // Verify response body + const result = res.body; + + expect(result.error).toBe('You are not allowed to access this content!'); + }); + afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection @@ -97,7 +114,9 @@ describe('Get all courses route', () => { // Send a get request to the courses endpoint const res = await request(`http://localhost:${PORT}`) - .get('/api/courses'); + .get('/api/courses') + .set('token', signAccessToken({ id: ADMIN_ID })); + expect(res.statusCode).toEqual(200); let i = 0; res.body.forEach(course => { expect(course).toMatchObject({ @@ -119,6 +138,18 @@ describe('Get all courses route', () => { }); }); + it('Returns error 401 if user is not authorized to access', async () => { + // Send a get request to the courses endpoint + const res = await request(`http://localhost:${PORT}`) + .get(`/api/courses/creator/${fakeUser._id}`) + .set('token', signAccessToken({ id: 'notAuthorized' })); + expect(res.statusCode).toEqual(401); + // Verify response body + const result = res.body; + + expect(result.error).toBe('You are not allowed to access this content!'); + }); + afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json deleted file mode 100644 index b3154bb1..00000000 --- a/__tests__/setup/globalConfigMongo.json +++ /dev/null @@ -1 +0,0 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:51309/"} \ No newline at end of file diff --git a/models/Courses.js b/models/Courses.js index 28904ae6..85cf47e0 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -32,9 +32,7 @@ const courseSchema = new Schema({ ref: 'User' }, difficulty: { - Number, - Range: [1, 5], - default: 3, + type: Number, }, status: { type: String, @@ -44,8 +42,6 @@ const courseSchema = new Schema({ estimatedHours: Number, rating: { Number, - Range: [0.0, 5.0], - default: 0.0, }, numOfSubscriptions:{ type: Number, From adba280e3ed1f51745994525d6b9150f62a304d9 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 11 Oct 2023 11:51:59 +0200 Subject: [PATCH 082/129] fix --- __tests__/fixtures/fakeUser.js | 2 +- __tests__/routes/courseRoutes.specs.js | 25 ++++++++++++++----------- __tests__/setup/globalConfigMongo.json | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index 538f42d6..9178b4f9 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -9,7 +9,7 @@ module.exports = function makeFakeUser() { joinedAt: new Date(), modifiedAt: new Date(), subscriptions: [ - { _id: 'testSub123'} + ] }; }; diff --git a/__tests__/routes/courseRoutes.specs.js b/__tests__/routes/courseRoutes.specs.js index 8ede94ec..eea6106b 100644 --- a/__tests__/routes/courseRoutes.specs.js +++ b/__tests__/routes/courseRoutes.specs.js @@ -152,19 +152,23 @@ describe('Course Routes', () => { describe('GET /users', () => { - it('should check if a user is subscribed to a specific course', async () => { - - const courseId = 'testSub123'; - - const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); - const userId = user._id; - + it('should check if a user is subscribed to a specific course and return true', async () => { + const courseId = '651d3a15cda7d5bd2878dfc7'; + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // Add the course to the user's subscriptions + await db.collection('users').findOneAndUpdate( + { _id: userId }, + { $push: { subscriptions: courseId } } + ); + const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); - + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + expect(response.status).toBe(200); expect(response.text).toBe('true'); - }); it('should return false if a user is not subscribed to a specific course', async () => { @@ -172,7 +176,6 @@ describe('Course Routes', () => { const course = await db.collection('courses').findOne({ title: 'test course'}); const courseId = course._id; - const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); const userId = user._id; diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index de4f9b5e..f5df2552 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:59260/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:59752/"} \ No newline at end of file From 26265fa1bb3a3dde6c7df1c714d288fc4718e831 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Wed, 11 Oct 2023 13:47:48 +0200 Subject: [PATCH 083/129] feat(api): Added routes for resetting password MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes made: โ€ข Added keys for gmail user (should be updated in .env) โ€ขย Added a new function for creating a reset password email in email.js โ€ข Added an error code for a failed attempt at sending email โ€ขย Added a new schema for PasswordResetTokens โ€ข Moved the /auth part of authRoutes' URLs to index.js โ€ข Added reset password request route โ€ขย Added reset password route โ€ข Added functions to generate a 4 digit code for resetting password --- config/keys.js | 3 ++ helpers/email.js | 29 +++++++++++--- helpers/errorCodes.js | 4 ++ models/PasswordResetToken.js | 25 ++++++++++++ routes/authRoutes.js | 78 +++++++++++++++++++++++++++++++++--- routes/index.js | 2 +- 6 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 models/PasswordResetToken.js diff --git a/config/keys.js b/config/keys.js index 5c469bf6..cfd943f0 100644 --- a/config/keys.js +++ b/config/keys.js @@ -15,6 +15,9 @@ const keys = { cookieKey: process.env.COOKIE_KEY, bucketKey: process.env.GOOGLE_APPLICATION_CREDENTIALS, TOKEN_SECRET: process.env.TOKEN_SECRET, + GMAIL_USER: process.env.GMAIL_USER, + GMAIL_PASSWORD: process.env.GMAIL_PASSWORD, + GMAIL_APP_PASSWORD: process.env.GMAIL_APP_PASSWORD, }; module.exports = keys; \ No newline at end of file diff --git a/helpers/email.js b/helpers/email.js index 6e5f4d86..cfccf2f2 100644 --- a/helpers/email.js +++ b/helpers/email.js @@ -1,14 +1,14 @@ const keys = require('../config/keys'); const nodemailer = require('nodemailer'); +const regexPatterns = require('./patterns'); module.exports = Object.freeze({ isValid, - send: sendMail + sendResetPasswordEmail, }); function isValid(email) { - const regEx = new RegExp('^[0-9a-zA-Z.]+@[a-zA-Z]+.[a-zA-Z]{2,4}'); - return regEx.test(email); + return regexPatterns.email.test(email); } @@ -24,8 +24,8 @@ async function sendMail({ service: 'gmail', auth: { type: 'login', - user: keys.gmailUser, - pass: keys.gmailAppPass, + user: keys.GMAIL_USER, + pass: keys.GMAIL_APP_PASSWORD, }, tls: { @@ -44,6 +44,25 @@ async function sendMail({ await transporter.sendMail(mailOptions); } +function sendResetPasswordEmail(user, token) { + const subject = 'Reset password request for Educado'; + const to = user.email; + const text = `Hi ${user.firstName}, + You have requested to reset your password. Please enter the following code in the app to reset your password: + ${token} + If you did not request this, please ignore this email and your password will remain unchanged. + Best regards, + The Educado team + `; + const html = ` +

Hi ${user.firstName},

+

You have requested to reset your password. Please enter the following code in the app to reset your password:

+

${token}

+

If you did not request this, please ignore this email and your password will remain unchanged.

+ `; + sendMail({ subject, to, text, html }); + return true; +} diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index 7227cc66..c3116b7f 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -17,6 +17,10 @@ module.exports = { code: 'E0003', message: 'Server could not be reached' }, + E0004: { + code: 'E0004', + message: 'Email could not be sent' + }, // E01 - Login errors E0101: { diff --git a/models/PasswordResetToken.js b/models/PasswordResetToken.js new file mode 100644 index 00000000..caa790b7 --- /dev/null +++ b/models/PasswordResetToken.js @@ -0,0 +1,25 @@ +// Mongoose model class for Courses +const mongoose = require('mongoose'); +const password = require('../helpers/password'); +const { Schema } = mongoose; + +// Class description +const passwordResetTokenSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User', + }, + token: { + type: String, + required: true, + }, + expiresAt: { + type: Date, + required: true, + }, +}); + +const PasswordResetToken = mongoose.model('passwordResetToken', passwordResetTokenSchema); + +module.exports = { PasswordResetToken }; \ No newline at end of file diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 37f3de1f..32784111 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -4,13 +4,16 @@ const { User } = require('../models/User'); // Import User model const { makeExpressCallback } = require('../helpers/express'); const { authEndpointHandler } = require('../auth'); const { signAccessToken } = require('../helpers/token'); -const { compare } = require('../helpers/password'); +const { compare, encrypt } = require('../helpers/password'); const errorCodes = require('../helpers/errorCodes'); +const send = require('send'); +const { sendResetPasswordEmail } = require('../helpers/email'); +const { PasswordResetToken } = require('../models/PasswordResetToken'); // Services //require("../services/passport"); -router.post('/auth', makeExpressCallback(authEndpointHandler)); +router.post('/', makeExpressCallback(authEndpointHandler)); /* Commented out until google login is implemented correctly // Route handler for login simulation @@ -32,7 +35,7 @@ router.get("/auth/google/callback", */ // Login -router.post('/auth/login', async (req, res) => { +router.post('/login', async (req, res) => { try { console.log(req.body); // Searching for a single user in the database, with the email provided in the request body @@ -70,18 +73,83 @@ router.post('/auth/login', async (req, res) => { } }); +router.post('/reset-password-request', async (req, res) => { + const { email } = req.body; + const user = await User.findOne({ email: email }); + if (!email || !user) { + return res.status(400).json({ error: errorCodes['E0401'] }); + } + + let token = await PasswordResetToken.findOne({ userId: user._id }); + if (token) await token.deleteOne(); + let resetToken = generatePasswordResetToken(); + console.log(resetToken) + const hash = await encrypt(resetToken); + + await new PasswordResetToken({ + userId: user._id, + token: hash, + expiresAt: Date.now() + 1000 * 60 * 5 // 2 minutes + }).save(); + + const success = await sendResetPasswordEmail(user, resetToken); + if(success) { + return res.status(200).json({ status: 'success' }); + } else { + return res.status(500).json({ error: errorCodes['E0004'] }); + } +}); + +router.put('/reset-password', async (req, res) => { + const { email, token, newPassword } = req.body; + const user = await User.findOne({ email: email }); + + if (!email || !user) { + return res.status(400).json({ error: errorCodes['E0401'] }); + } + const passwordResetToken = await PasswordResetToken.findOne({ userId: user._id}); + if (!passwordResetToken || passwordResetToken.expiresAt < Date.now()) { + return res.status(400).json({ error: errorCodes['E0404'] }); + } + const isValid = await compare(token, passwordResetToken.token); + if (!isValid) { + return res.status(400).json({ error: errorCodes['E0405'] }); + } + + user.password = await encrypt(newPassword); + await User.updateOne({ _id: user._id }, user); + await passwordResetToken.deleteOne(); + + return res.status(200).json({ status: 'success' }); + +}); // Logout simulation -router.get('/auth/logout', (req, res) => { +router.get('/logout', (req, res) => { req.logout(); res.redirect('/'); }); // Show current user simulation -router.get('/auth/current_user', (req, res) => { +router.get('/current_user', (req, res) => { setTimeout(() => { res.send(req.user); }, 1500); }); module.exports = router; + +function generatePasswordResetToken() { + const length = 4; + let retVal = ''; + for (let i = 0; i < length; i++) { + retVal += getRandomNumber(0, 9); + } + return retVal; +} + +function getRandomNumber(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 76a4763e..83d499fe 100644 --- a/routes/index.js +++ b/routes/index.js @@ -18,7 +18,7 @@ router.get('/api', (req, res) => { router.use('/api', CourseRoutes); router.use('', AWSRoutes); -router.use('/api', AuthRoutes); +router.use('/api/auth', AuthRoutes); router.use('/api/signup', SignupRoutes); router.use('/api/applications', ApplicationRoutes); router.use('/api/mail', MailRoutes); From b2dfadc431c9eca224db4abf0862a1a8c5afa3b7 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 11 Oct 2023 14:42:41 +0200 Subject: [PATCH 084/129] test!! final --- __tests__/routes/courseRoutes.specs.js | 50 ++++++++++++++++++------ __tests__/setup/globalConfigMongo.json | 2 +- routes/courseRoutes.js | 53 +++++++++++++++----------- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/__tests__/routes/courseRoutes.specs.js b/__tests__/routes/courseRoutes.specs.js index eea6106b..bf81e75f 100644 --- a/__tests__/routes/courseRoutes.specs.js +++ b/__tests__/routes/courseRoutes.specs.js @@ -47,6 +47,7 @@ describe('Course Routes', () => { .get('/api/courses'); expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Array); + }); }); @@ -61,6 +62,7 @@ describe('Course Routes', () => { expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); + expect(response.body._id.toString()).toBe(courseId.toString()); }); }); @@ -110,9 +112,13 @@ describe('Course Routes', () => { .post('/api/courses/' + courseId + '/subscribe') .send({ user_id: userId}); + console.log("course id: " + courseId) + console.log('response.body.subscriptions:', response.body.subscriptions) + console.log(response.body.subscriptions.find((element) => element == courseId)) + expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); - + expect(response.body.subscriptions.find((element) => element == courseId)); }); }); @@ -131,6 +137,7 @@ describe('Course Routes', () => { expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); + expect(response.body.subscriptions.find((element) => element !== courseId)); }); }); @@ -138,14 +145,28 @@ describe('Course Routes', () => { describe('GET /users/:id/subscriptions', () => { it('should get user subscriptions', async () => { - const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const courseId = '651d3a15cda7d5bd2878dfc7'; + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); const userId = user._id; + + // Find the user and update their subscriptions + const result = await db.collection('users').findOneAndUpdate( + { _id: userId }, // Convert userId to ObjectId if needed + { $push: { subscriptions: courseId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + + const updatedUser = result.value; + + // Check if the subscription was successfully added + expect(updatedUser.subscriptions.includes(courseId)).toBe(true); const response = await request(`http://localhost:${PORT}`) .get('/api/users/' + userId + '/subscriptions'); expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Array); + expect(response.body.find((element) => element == courseId)); }); }); @@ -154,22 +175,29 @@ describe('Course Routes', () => { it('should check if a user is subscribed to a specific course and return true', async () => { const courseId = '651d3a15cda7d5bd2878dfc7'; - const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); const userId = user._id; - - // Add the course to the user's subscriptions - await db.collection('users').findOneAndUpdate( - { _id: userId }, - { $push: { subscriptions: courseId } } + + // Find the user and update their subscriptions + const result = await db.collection('users').findOneAndUpdate( + { _id: userId }, // Convert userId to ObjectId if needed + { $push: { subscriptions: courseId } }, + { returnDocument: 'after' } // 'after' returns the updated document ); - + + const updatedUser = result.value; + + // Check if the subscription was successfully added + expect(updatedUser.subscriptions.includes(courseId)).toBe(true); + const response = await request(`http://localhost:${PORT}`) .get('/api/users?user_id=' + userId + '&course_id=' + courseId); - + expect(response.status).toBe(200); expect(response.text).toBe('true'); - }); + }); + + it('should return false if a user is not subscribed to a specific course', async () => { diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index f5df2552..b2a26bca 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:59752/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:62476/"} \ No newline at end of file diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 76f3f7cc..c7ef5e24 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -533,7 +533,7 @@ router.get("/courses/:id/sections", async (req, res) => { // Get a specififc section router.get("/courses/:courseId/sections/:sectionId", async (req, res) => { - try{ + try { const { courseId, sectionId } = req.params; // find a specific section within the given course by both IDs @@ -583,7 +583,9 @@ router.post("/courses/:id/subscribe", async (req, res) => { .save; let user = await User.findById(user_id); - res.send(user) + + console.log(user); + res.send(user); } catch (error) { console.error(error); @@ -606,6 +608,8 @@ router.post("/courses/:id/unsubscribe", async (req, res) => { .save; let user = await User.findById(user_id); + + console.log(id) res.send(user) } catch (error) { @@ -619,8 +623,9 @@ router.post("/courses/:id/unsubscribe", async (req, res) => { router.get("/users/:id/subscriptions", async (req, res) => { try { const userId = req.params.id; + // Find the user by _id and select the 'subscriptions' field - const user = await User.findById(userId).select('subscriptions -_id'); + const user = await User.findById(userId).select('subscriptions'); if (!user) { return res.status(404).json({ message: 'User not found' }); @@ -631,7 +636,10 @@ router.get("/users/:id/subscriptions", async (req, res) => { // Find courses based on the subscribed course IDs const list = await CourseModel.find({ '_id': { $in: subscribedCourses } }); + console.log(userId); + console.log(list); res.send(list); + } catch (error) { console.error(error); res.status(500).json({ message: 'Server error' }); @@ -642,25 +650,24 @@ router.get("/users/:id/subscriptions", async (req, res) => { // Checks if user is subscribed to a specific course router.get('/users', async (req, res) => { - - try { - - const { course_id, user_id } = req.query; - - // checks if the course id exist in the users subscriptions field - const user = await User.findOne({ _id: user_id, subscriptions: course_id }); - - // return true if it exist and false if it does not - if(user == null) { - res.send("false"); - } else { - res.send("true"); - } - - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } -}); + try { + const { user_id, course_id } = req.query; + + // Check if the course_id exists in the user's subscriptions array + const userTest = await User.findById(user_id); + + if (userTest.subscriptions.includes(course_id)) { + // User is subscribed to the course + res.send("true"); + } else { + // User is not subscribed to the course + res.send("false"); + } + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + }); + module.exports = router; From 8ddd367e0433a8b7e4bc5b1f437d1bba8a66ea6d Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Wed, 11 Oct 2023 15:02:49 +0200 Subject: [PATCH 085/129] fix model --- models/Courses.js | 5 ++++- models/Exercises.js | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/models/Courses.js b/models/Courses.js index 35071825..6c31dec2 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -16,7 +16,10 @@ const courseSchema = new Schema({ category: String, published: Boolean, difficulty: Number, - time: Number, + status: String, + rating: Number, + numOfSubscriptions: Number, + estimatedHours: Number, sections: [{ type: Schema.Types.ObjectId, ref: "sections" }], creator: { type: Schema.Types.ObjectId, ref: "contentCreator" } }); diff --git a/models/Exercises.js b/models/Exercises.js index e38c8d8f..3b0cb9ef 100644 --- a/models/Exercises.js +++ b/models/Exercises.js @@ -10,9 +10,9 @@ const exerciseSchema = new Schema ({ answers: { text: String, correct: Boolean, - modifiedAt: Date + modifiedAt: Date, + feedBack: String }, - onWrongFeedback: component, modifiedAt: Date, }) From d6e4fcfa9dfb8d11b2efbbe7d3041e80b71decd4 Mon Sep 17 00:00:00 2001 From: MaizeHH <92512497+MaizeHH@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:45:26 +0200 Subject: [PATCH 086/129] email case insensitive - email is now case insensitive - done by putting lowercase field in User model to true --- models/User.js | 1 + 1 file changed, 1 insertion(+) diff --git a/models/User.js b/models/User.js index 83772c10..858241fa 100644 --- a/models/User.js +++ b/models/User.js @@ -45,6 +45,7 @@ const userSchema = new Schema({ required: [true, 'Email is required'], minLength: [6, 'Email must be at least 6 characters'], unique: [true, 'Email must be unique'], + lowercase: true, validate: { validator: (email) => { /** From d161d61867dde151f1257529f7e07fb046bbbe15 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Thu, 12 Oct 2023 09:11:25 +0200 Subject: [PATCH 087/129] Using patch instead of put for updating --- __tests__/routes/userRoutes.spec.js | 40 ++++++++++++++--------------- routes/userRoutes.js | 12 ++++----- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 8cedf4bb..9170a223 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -71,40 +71,40 @@ describe('Update User Email Route', () => { it('updates user email successfully', async () => { const newEmail = 'newemail@example.com'; - + await request(`http://localhost:${PORT}`) - .put('/api/user/update-email/' + fakeUser._id) + .patch('/api/user/update-email/' + fakeUser._id) .set('token', token) // Include the token in the request headers .send({ newEmail }) - .expect(200); - + .expect(200); // Expecting a 200 OK response + // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ email: newEmail }); - expect(user).toBeDefined(); - expect(user.email).toBe(newEmail); + const user = await db.collection('users').findOne({ email: newEmail }); + expect(user).toBeDefined(); + expect(user.email).toBe(newEmail); }); - + it('Test that emails must be unique when updating', async () => { const newEmail = fakeUser.email; - + const response = await request(`http://localhost:${PORT}`) - .put('/api/user/update-email/' + fakeUser._id) + .patch('/api/user/update-email/' + fakeUser._id) .set('token', token) // Include the token in the request headers .send({ newEmail }) - .expect(400); - - expect(response.body.error.code).toBe('E0201'); + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0201'); }); - + it('handles user not found error for update-email', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; - + await request(`http://localhost:${PORT}`) - .put('/api/user/update-email/' + nonExistentUserId) + .patch('/api/user/update-email/' + nonExistentUserId) .set('token', token) // Include the token in the request headers .send({ newEmail }) - .expect(204); + .expect(204); // Expecting a 204 No Content response for user not found }); it('updates user first name successfully', async () => { @@ -117,7 +117,7 @@ describe('Update User Email Route', () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) - .put(`/api/user/update-first-name/${nonExistentUserId}`) + .patch(`/api/user/update-first-name/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) .expect(204); @@ -133,7 +133,7 @@ describe('Update User Email Route', () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) - .put(`/api/user/update-last-name/${nonExistentUserId}`) + .patch(`/api/user/update-last-name/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newLastName: 'NewLastName' }) .expect(204); @@ -147,7 +147,7 @@ describe('Update User Email Route', () => { const updateUserProperty = async (route, propertyName, propertyValue, dbField) => { await request(`http://localhost:${PORT}`) - .put(`/api/user/update-${route}/${fakeUser._id}`) + .patch(`/api/user/update-${route}/${fakeUser._id}`) .set('token', token) // Include the token in the request headers .send({ [propertyName]: propertyValue }) .expect(200); diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 9fc6b529..a6c724f4 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -33,7 +33,7 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { }); // Update User Email route -router.put('/update-email/:id', requireLogin, async (req, res) => { +router.patch('/update-email/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const { newEmail } = req.body; @@ -52,7 +52,7 @@ router.put('/update-email/:id', requireLogin, async (req, res) => { } res.status(200); - res.send(updatedUser) + res.send(updatedUser); } } catch (error) { @@ -65,13 +65,13 @@ router.put('/update-email/:id', requireLogin, async (req, res) => { console.log(error); res.send({ - error: error - }); + error: error + }); } }); // Update User first name route -router.put('/update-first-name/:id', requireLogin, async (req, res) => { +router.patch('/update-first-name/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const { newFirstName } = req.body; @@ -109,7 +109,7 @@ router.put('/update-first-name/:id', requireLogin, async (req, res) => { }); // Update User last name route -router.put('/update-last-name/:id', requireLogin, async (req, res) => { +router.patch('/update-last-name/:id', requireLogin, async (req, res) => { try { const { id } = req.params; const { newLastName } = req.body; From 49fde303096e91cda5861a1e45e8681d8260b297 Mon Sep 17 00:00:00 2001 From: Mafusn Date: Thu, 12 Oct 2023 10:05:27 +0200 Subject: [PATCH 088/129] Last cleanup before merge --- __tests__/routes/userRoutes.spec.js | 119 +++++++++++++++++++++------- routes/index.js | 2 +- routes/userRoutes.js | 102 ++++++------------------ 3 files changed, 116 insertions(+), 107 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 9170a223..b56a294a 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -8,7 +8,7 @@ const mongoose = require('mongoose'); const app = express(); app.use(express.json()); -app.use('/api/user', router); // Mount the router under '/api' path +app.use('/api/users', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5023; // Choose a port for testing @@ -51,7 +51,7 @@ describe('Update User Email Route', () => { it('deletes a user successfully', async () => { // Delete the user using the API await request(`http://localhost:${PORT}`) - .delete(`/api/user/delete/${fakeUser._id}`) + .delete(`/api/users/delete/${fakeUser._id}`) .set('token', token) // Include the token in the request headers .expect(200); @@ -64,7 +64,7 @@ describe('Update User Email Route', () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) - .delete(`/api/user/delete/${nonExistentUserId}`) + .delete(`/api/users/delete/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .expect(204); }); @@ -73,13 +73,13 @@ describe('Update User Email Route', () => { const newEmail = 'newemail@example.com'; await request(`http://localhost:${PORT}`) - .patch('/api/user/update-email/' + fakeUser._id) + .patch('/api/users/' + fakeUser._id) .set('token', token) // Include the token in the request headers - .send({ newEmail }) + .send({ email: newEmail }) .expect(200); // Expecting a 200 OK response // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ email: newEmail }); + const user = await db.collection('users').findOne({ _id: fakeUser._id }); expect(user).toBeDefined(); expect(user.email).toBe(newEmail); }); @@ -88,9 +88,9 @@ describe('Update User Email Route', () => { const newEmail = fakeUser.email; const response = await request(`http://localhost:${PORT}`) - .patch('/api/user/update-email/' + fakeUser._id) + .patch('/api/users/' + fakeUser._id) .set('token', token) // Include the token in the request headers - .send({ newEmail }) + .send({ email: newEmail }) .expect(400); // Expecting a 400 Bad Request response expect(response.body.error.code).toBe('E0201'); @@ -101,23 +101,32 @@ describe('Update User Email Route', () => { const newEmail = 'newemail@example.com'; await request(`http://localhost:${PORT}`) - .patch('/api/user/update-email/' + nonExistentUserId) + .patch('/api/users/' + nonExistentUserId) .set('token', token) // Include the token in the request headers - .send({ newEmail }) + .send({ email: newEmail }) .expect(204); // Expecting a 204 No Content response for user not found }); it('updates user first name successfully', async () => { const newFirstName = 'newFirstName'; - await updateUserProperty('first-name', newFirstName, newFirstName, 'firstName'); + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ firstName: newFirstName }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.firstName).toBe(newFirstName); }); it('handles user not found error for update-first-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) - .patch(`/api/user/update-first-name/${nonExistentUserId}`) + .patch(`/api/users/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) .expect(204); @@ -126,35 +135,91 @@ describe('Update User Email Route', () => { it('updates user last name successfully', async () => { const newLastName = 'newLastName'; - await updateUserProperty('last-name', newLastName, newLastName, 'lastName'); + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ lastName: newLastName }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.lastName).toBe(newLastName); }); it('handles user not found error for update-last-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) - .patch(`/api/user/update-last-name/${nonExistentUserId}`) + .patch(`/api/users/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .send({ newLastName: 'NewLastName' }) .expect(204); }); + it('updates user fields successfully', async () => { + const newEmail = 'newemail@example.com'; + const newFirstName = 'Jane'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + email: newEmail, + firstName: newFirstName + }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was updated in the database + const updatedUser = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(updatedUser.email).toBe(newEmail); + expect(updatedUser.firstName).toBe(newFirstName); + }); + + it('handles user not found error', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers + .send({ + email: newEmail + }) + .expect(204); // Expecting a 204 No Content response for user not found + }); + + it('handles validation errors for email', async () => { + const invalidEmail = 'invalidemail'; // Invalid email format + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + email: invalidEmail + }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0206'); + }); + + it('handles validation errors for first name', async () => { + const invalidFirstName = 'AASD!==#ยค("DSN:_;>:'; // Invalid email format + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + firstName: invalidFirstName + }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0211'); + }); + afterAll(async () => { db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection server.close(); await mongoose.connection.close(); }); - - const updateUserProperty = async (route, propertyName, propertyValue, dbField) => { - await request(`http://localhost:${PORT}`) - .patch(`/api/user/update-${route}/${fakeUser._id}`) - .set('token', token) // Include the token in the request headers - .send({ [propertyName]: propertyValue }) - .expect(200); - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ [dbField]: propertyValue }); - expect(user).toBeDefined(); - expect(user[dbField]).toBe(propertyValue); - }; }); \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 76a4763e..edf26d22 100644 --- a/routes/index.js +++ b/routes/index.js @@ -22,7 +22,7 @@ router.use('/api', AuthRoutes); router.use('/api/signup', SignupRoutes); router.use('/api/applications', ApplicationRoutes); router.use('/api/mail', MailRoutes); -router.use('/api/user', UserRoutes); +router.use('/api/users', UserRoutes); // Test route router.use('/api/test', TestRoutes); diff --git a/routes/userRoutes.js b/routes/userRoutes.js index a6c724f4..4554311f 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -32,18 +32,18 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { } }); -// Update User Email route -router.patch('/update-email/:id', requireLogin, async (req, res) => { +// Update User with dynamic fields +router.patch('/:id', requireLogin, async (req, res) => { try { const { id } = req.params; - const { newEmail } = req.body; + const updateFields = req.body; // Fields to be updated dynamically - const emailValid = await validateEmail(newEmail); + const validFields = await validateFields(updateFields); - if (emailValid) { + if (validFields) { const updatedUser = await User.findByIdAndUpdate( id, - { email: newEmail }, + { $set: updateFields }, { new: true } // This ensures that the updated user document is returned ); @@ -51,46 +51,7 @@ router.patch('/update-email/:id', requireLogin, async (req, res) => { throw errorCodes['E0004']; } - res.status(200); - res.send(updatedUser); - } - - } catch (error) { - if (error === errorCodes['E0004']) { - // Handle "user not found" error response here - res.status(204); - } else { - res.status(400); - } - - console.log(error); - res.send({ - error: error - }); - } -}); - -// Update User first name route -router.patch('/update-first-name/:id', requireLogin, async (req, res) => { - try { - const { id } = req.params; - const { newFirstName } = req.body; - - const nameValid = await validateName(newFirstName); - - if (nameValid) { - const updatedUser = await User.findByIdAndUpdate( - id, - { firstName: newFirstName }, - { new: true } // This ensures that the updated user document is returned - ); - - if (!updatedUser) { - throw errorCodes['E0004']; - } - - res.status(200); - res.send(updatedUser) + res.status(200).send(updatedUser); } } catch (error) { @@ -108,42 +69,25 @@ router.patch('/update-first-name/:id', requireLogin, async (req, res) => { } }); -// Update User last name route -router.patch('/update-last-name/:id', requireLogin, async (req, res) => { - try { - const { id } = req.params; - const { newLastName } = req.body; - - const nameValid = await validateName(newLastName); - - if (nameValid) { - const updatedUser = await User.findByIdAndUpdate( - id, - { lastName: newLastName }, - { new: true } // This ensures that the updated user document is returned - ); +async function validateFields(fields) { + const fieldEntries = Object.entries(fields); - if (!updatedUser) { - throw errorCodes['E0004']; + for (const [fieldName, fieldValue] of fieldEntries) { + if (fieldName === 'email') { + const emailValid = await validateEmail(fieldValue); + if (!emailValid) { + return false; + } + } else if (fieldName === 'firstName' || fieldName === 'lastName') { + const nameValid = await validateName(fieldValue); + if (!nameValid) { + return false; } - - res.status(200); - res.send(updatedUser) - } - - } catch (error) { - if (error === errorCodes['E0004']) { - // Handle "user not found" error response here - res.status(204); - } else { - res.status(400); } - - console.log(error); - res.send({ - error: error - }); } -}); + return true; +} + + module.exports = router; \ No newline at end of file From f0eda5a254f7c9b6d2fd193b6639fd77590bd56c Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 11:13:23 +0200 Subject: [PATCH 089/129] model fix --- models/ContentCreatorApplication.js | 2 +- models/Courses.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/ContentCreatorApplication.js b/models/ContentCreatorApplication.js index 7b688973..f9e893b6 100644 --- a/models/ContentCreatorApplication.js +++ b/models/ContentCreatorApplication.js @@ -15,7 +15,7 @@ const ContentCreatorSchema = new Schema({ }); const ContentCreator = mongoose.model( - 'ContentCreator', + 'contentCreator', ContentCreatorSchema ); diff --git a/models/Courses.js b/models/Courses.js index 6c31dec2..770781f3 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -1,7 +1,7 @@ // Mongoose model class for Courses const mongoose = require('mongoose'); const { Schema } = mongoose; -const { component } = require("./Components"); +const { ComponentModel } = require("./Components"); // Routes are sorted into COURSE - SECTION - COMPONENT each with ASCII art, within each functions are in order of CRUD // NOTE Files do NOT delete from the backend yet, on the TODO as of 03/2022 @@ -12,7 +12,7 @@ const courseSchema = new Schema({ description: String, dateCreated: Date, dateUpdated: Date, - coverImg: component, + coverImg: ComponentModel, category: String, published: Boolean, difficulty: Number, From 405835a9f3dceb5e04b772c3b6c11d207eb7cd30 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 11:34:26 +0200 Subject: [PATCH 090/129] fix --- __tests__/setup/globalConfigMongo.json | 2 +- models/Components.js | 20 ++++++--------- models/Courses.js | 8 +++--- models/Exercises.js | 34 +++++++++++--------------- models/Sections.js | 3 ++- 5 files changed, 28 insertions(+), 39 deletions(-) diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index d4260853..a2af268b 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:62476/"} +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:64546/"} \ No newline at end of file diff --git a/models/Components.js b/models/Components.js index 9bf01017..d48cea05 100644 --- a/models/Components.js +++ b/models/Components.js @@ -1,16 +1,12 @@ -// Mongoose model class for Courses` -const mongoose = require('mongoose'); +const mongoose = require("mongoose"); const { Schema } = mongoose; -// Class description -const componentsSchema = new Schema({ - type: String, // Image / Video / Audio / Text - file: String, // AWS URL, if video, audio or image - text: String, // IF component is text - dateCreated: Date, // For all components - dateUpdated: Date, // If its a text component +const component = new Schema({ + _id: Schema.Types.ObjectId, + fileName: String, + path: String, + size: Number, + type: String, }); -const ComponentModel = mongoose.model('components', componentsSchema); - -module.exports = { ComponentModel } +module.exports = { component }; diff --git a/models/Courses.js b/models/Courses.js index 770781f3..0a72162a 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -1,10 +1,8 @@ // Mongoose model class for Courses const mongoose = require('mongoose'); +const { component } = require('./Components'); const { Schema } = mongoose; -const { ComponentModel } = require("./Components"); -// Routes are sorted into COURSE - SECTION - COMPONENT each with ASCII art, within each functions are in order of CRUD -// NOTE Files do NOT delete from the backend yet, on the TODO as of 03/2022 // Class description const courseSchema = new Schema({ @@ -12,7 +10,7 @@ const courseSchema = new Schema({ description: String, dateCreated: Date, dateUpdated: Date, - coverImg: ComponentModel, + coverImg: component, category: String, published: Boolean, difficulty: Number, @@ -21,7 +19,7 @@ const courseSchema = new Schema({ numOfSubscriptions: Number, estimatedHours: Number, sections: [{ type: Schema.Types.ObjectId, ref: "sections" }], - creator: { type: Schema.Types.ObjectId, ref: "contentCreator" } + author: { type: Schema.Types.ObjectId, ref: "contentCreator" }, }); const CourseModel = mongoose.model( diff --git a/models/Exercises.js b/models/Exercises.js index 3b0cb9ef..fa1c6adc 100644 --- a/models/Exercises.js +++ b/models/Exercises.js @@ -1,24 +1,18 @@ -const mongoose = require("mongoose") +// Mongoose model class for Courses +const mongoose = require("mongoose"); const { component } = require("./Components"); const { Schema } = mongoose; -const exerciseSchema = new Schema ({ - parentSection: Schema.Types.ObjectId, - title: String, - description: String, - content: component, - answers: { - text: String, - correct: Boolean, - modifiedAt: Date, - feedBack: String - }, - modifiedAt: Date, -}) +// Class description +const exerciseSchema = new Schema({ + parentSection: { type: Schema.Types.ObjectId, ref: "sections" }, + title: String, + description: String, + content: component, + answers: [{}], + modifiedAt: Date, +}); -const ExerciseModel = mongoose.model( - "exercises", - exerciseSchema - ); - - module.exports = { ExerciseModel } \ No newline at end of file +const ExerciseModel = mongoose.model("exercises", exerciseSchema); // Create new collection called courses, using the courseScema + +module.exports = { ExerciseModel } \ No newline at end of file diff --git a/models/Sections.js b/models/Sections.js index d29b9ee4..5cb0e7e0 100644 --- a/models/Sections.js +++ b/models/Sections.js @@ -1,5 +1,6 @@ // Mongoose model class for Courses const mongoose = require('mongoose'); +const { component } = require('./Components'); const { Schema } = mongoose; // Class description @@ -12,7 +13,7 @@ const sectionSchema = new Schema({ createdAt: Date, modifiedAt: Date, totalPoints: Number, - components: [{ type: Schema.Types.ObjectId, ref: "components" }], + components: component, parentCourse: { type: Schema.Types.ObjectId, ref: "courses" }, }); From 903e90e07040d480cb21fbc0bd464edf424b5bb1 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 13:24:20 +0200 Subject: [PATCH 091/129] route fix /api/courses in index.js, and moved user subscription to user tests --- ...seRoutes.specs.js => courseRoutes.spec.js} | 76 +--- __tests__/routes/signupRoutes.spec.js | 1 + __tests__/routes/userRoutes.spec.js | 416 +++++++++++------- __tests__/setup/globalConfigMongo.json | 2 +- ...ntentCreatorApplicationController_test.js} | 5 +- ...notInUseTest_contentCreatorApplication.js} | 0 routes/courseRoutes.js | 64 +-- routes/index.js | 2 +- routes/userRoutes.js | 51 +++ 9 files changed, 317 insertions(+), 300 deletions(-) rename __tests__/routes/{courseRoutes.specs.js => courseRoutes.spec.js} (65%) rename applications/content-creator-applications/controller/{contentCreatorApplicationControllerspec.js => contentCreatorApplicationController_test.js} (99%) rename applications/content-creator-applications/domain/{contentCreatorApplication.spec.js => notInUseTest_contentCreatorApplication.js} (100%) diff --git a/__tests__/routes/courseRoutes.specs.js b/__tests__/routes/courseRoutes.spec.js similarity index 65% rename from __tests__/routes/courseRoutes.specs.js rename to __tests__/routes/courseRoutes.spec.js index bf81e75f..eb6fc87f 100644 --- a/__tests__/routes/courseRoutes.specs.js +++ b/__tests__/routes/courseRoutes.spec.js @@ -13,7 +13,7 @@ const User = require('../../models/User'); const app = express(); app.use(express.json()); -app.use('/api', router); // Mount the router under '/api' path +app.use('/api/courses', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5021; // Choose a port for testing @@ -142,80 +142,6 @@ describe('Course Routes', () => { }); }); - describe('GET /users/:id/subscriptions', () => { - it('should get user subscriptions', async () => { - - const courseId = '651d3a15cda7d5bd2878dfc7'; - const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); - const userId = user._id; - - // Find the user and update their subscriptions - const result = await db.collection('users').findOneAndUpdate( - { _id: userId }, // Convert userId to ObjectId if needed - { $push: { subscriptions: courseId } }, - { returnDocument: 'after' } // 'after' returns the updated document - ); - - const updatedUser = result.value; - - // Check if the subscription was successfully added - expect(updatedUser.subscriptions.includes(courseId)).toBe(true); - - const response = await request(`http://localhost:${PORT}`) - .get('/api/users/' + userId + '/subscriptions'); - - expect(response.status).toBe(200); - expect(response.body).toBeInstanceOf(Array); - expect(response.body.find((element) => element == courseId)); - - }); - }); - - describe('GET /users', () => { - - it('should check if a user is subscribed to a specific course and return true', async () => { - const courseId = '651d3a15cda7d5bd2878dfc7'; - const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); - const userId = user._id; - - // Find the user and update their subscriptions - const result = await db.collection('users').findOneAndUpdate( - { _id: userId }, // Convert userId to ObjectId if needed - { $push: { subscriptions: courseId } }, - { returnDocument: 'after' } // 'after' returns the updated document - ); - - const updatedUser = result.value; - - // Check if the subscription was successfully added - expect(updatedUser.subscriptions.includes(courseId)).toBe(true); - - const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); - - expect(response.status).toBe(200); - expect(response.text).toBe('true'); - }); - - - - it('should return false if a user is not subscribed to a specific course', async () => { - - const course = await db.collection('courses').findOne({ title: 'test course'}); - const courseId = course._id; - - const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); - const userId = user._id; - - const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); - - expect(response.status).toBe(200); - expect(response.text).toBe('false'); - - }); - }); - afterAll(async () => { db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 0b68fa95..9595e128 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -20,6 +20,7 @@ const server = app.listen(PORT, () => { let fakeUser = makeFakeUser(); + describe('Signup User route', () => { let db; // Store the database connection diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index b56a294a..d708591d 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -1,8 +1,9 @@ const request = require('supertest'); const express = require('express'); const router = require('../../routes/userRoutes'); -const connectDb = require('../fixtures/db') -const makeFakeUser = require('../fixtures/fakeUser') +const connectDb = require('../fixtures/db'); +const makeFakeUser = require('../fixtures/fakeUser'); +const makeFakeCourse = require('../fixtures/fakeCourse'); const { signAccessToken } = require('../../helpers/token'); const mongoose = require('mongoose'); @@ -19,207 +20,292 @@ const server = app.listen(PORT, () => { // Mocked token secret const TOKEN_SECRET = 'test'; +// make fake course +let fakeCourse = makeFakeCourse(); + // Mock token secret jest.mock('../../config/keys', () => { - return { - TOKEN_SECRET - }; + return { + TOKEN_SECRET + }; }); +describe('Users Routes', () => { -describe('Update User Email Route', () => { - let token, fakeUser, db; + let token, fakeUser, db; beforeAll(async () => { - db = await connectDb(); // Connect to the database + db = await connectDb(); // Connect to the database - token = signAccessToken({ id: 1 }); - fakeUser = makeFakeUser(); + token = signAccessToken({ id: 1 }); + fakeUser = makeFakeUser(); + await db.collection('courses').insertOne(fakeCourse); - }); + }); beforeEach(async () => { - // Insert the fake user into the database before each test - await db.collection('users').insertOne(fakeUser); + // Insert the fake user into the database before each test + await db.collection('users').insertOne(fakeUser); }); afterEach(async () => { - // Remove the user from the database after each test - await db.collection('users').deleteOne({ _id: fakeUser._id }); - }); + // Remove the user from the database after each test + await db.collection('users').deleteOne({ _id: fakeUser._id }); + }); + + describe('Update User Email Route', () => { it('deletes a user successfully', async () => { - // Delete the user using the API - await request(`http://localhost:${PORT}`) - .delete(`/api/users/delete/${fakeUser._id}`) - .set('token', token) // Include the token in the request headers - .expect(200); - - // Verify that the user was deleted from the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeNull(); - }); + // Delete the user using the API + await request(`http://localhost:${PORT}`) + .delete(`/api/users/delete/${fakeUser._id}`) + .set('token', token) // Include the token in the request headers + .expect(200); + + // Verify that the user was deleted from the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeNull(); + }); it('handles user not found error for delete', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); + const nonExistentUserId = new mongoose.Types.ObjectId(); - await request(`http://localhost:${PORT}`) - .delete(`/api/users/delete/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .expect(204); + await request(`http://localhost:${PORT}`) + .delete(`/api/users/delete/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .expect(204); }); it('updates user email successfully', async () => { - const newEmail = 'newemail@example.com'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ email: newEmail }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeDefined(); - expect(user.email).toBe(newEmail); - }); - + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ email: newEmail }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.email).toBe(newEmail); + }); + it('Test that emails must be unique when updating', async () => { - const newEmail = fakeUser.email; - - const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ email: newEmail }) - .expect(400); // Expecting a 400 Bad Request response - - expect(response.body.error.code).toBe('E0201'); - }); - + const newEmail = fakeUser.email; + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ email: newEmail }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0201'); + }); + it('handles user not found error for update-email', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - const newEmail = 'newemail@example.com'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + nonExistentUserId) - .set('token', token) // Include the token in the request headers - .send({ email: newEmail }) - .expect(204); // Expecting a 204 No Content response for user not found + const nonExistentUserId = new mongoose.Types.ObjectId(); + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers + .send({ email: newEmail }) + .expect(204); // Expecting a 204 No Content response for user not found }); it('updates user first name successfully', async () => { - const newFirstName = 'newFirstName'; + const newFirstName = 'newFirstName'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ firstName: newFirstName }) + .expect(200); // Expecting a 200 OK response - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ firstName: newFirstName }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeDefined(); - expect(user.firstName).toBe(newFirstName); + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.firstName).toBe(newFirstName); }); it('handles user not found error for update-first-name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - - await request(`http://localhost:${PORT}`) - .patch(`/api/users/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .send({ newFirstName: 'NewFirstName' }) - .expect(204); - }); - + const nonExistentUserId = new mongoose.Types.ObjectId(); + + await request(`http://localhost:${PORT}`) + .patch(`/api/users/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .send({ newFirstName: 'NewFirstName' }) + .expect(204); + }); + it('updates user last name successfully', async () => { - const newLastName = 'newLastName'; + const newLastName = 'newLastName'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ lastName: newLastName }) + .expect(200); // Expecting a 200 OK response - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ lastName: newLastName }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeDefined(); - expect(user.lastName).toBe(newLastName); + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.lastName).toBe(newLastName); }); it('handles user not found error for update-last-name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); + const nonExistentUserId = new mongoose.Types.ObjectId(); - await request(`http://localhost:${PORT}`) - .patch(`/api/users/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .send({ newLastName: 'NewLastName' }) - .expect(204); + await request(`http://localhost:${PORT}`) + .patch(`/api/users/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .send({ newLastName: 'NewLastName' }) + .expect(204); }); it('updates user fields successfully', async () => { - const newEmail = 'newemail@example.com'; - const newFirstName = 'Jane'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ - email: newEmail, - firstName: newFirstName - }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was updated in the database - const updatedUser = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(updatedUser.email).toBe(newEmail); - expect(updatedUser.firstName).toBe(newFirstName); - }); - - it('handles user not found error', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - const newEmail = 'newemail@example.com'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + nonExistentUserId) - .set('token', token) // Include the token in the request headers - .send({ - email: newEmail - }) - .expect(204); // Expecting a 204 No Content response for user not found - }); - - it('handles validation errors for email', async () => { - const invalidEmail = 'invalidemail'; // Invalid email format - - const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ - email: invalidEmail - }) - .expect(400); // Expecting a 400 Bad Request response - - expect(response.body.error.code).toBe('E0206'); - }); - - it('handles validation errors for first name', async () => { - const invalidFirstName = 'AASD!==#ยค("DSN:_;>:'; // Invalid email format - - const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ - firstName: invalidFirstName - }) - .expect(400); // Expecting a 400 Bad Request response - - expect(response.body.error.code).toBe('E0211'); - }); - - afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection - server.close(); - await mongoose.connection.close(); - }); + const newEmail = 'newemail@example.com'; + const newFirstName = 'Jane'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + email: newEmail, + firstName: newFirstName + }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was updated in the database + const updatedUser = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(updatedUser.email).toBe(newEmail); + expect(updatedUser.firstName).toBe(newFirstName); + }); + + it('handles user not found error', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers + .send({ + email: newEmail + }) + .expect(204); // Expecting a 204 No Content response for user not found + }); + + it('handles validation errors for email', async () => { + const invalidEmail = 'invalidemail'; // Invalid email format + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + email: invalidEmail + }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0206'); + }); + + it('handles validation errors for first name', async () => { + const invalidFirstName = 'AASD!==#ยค("DSN:_;>:'; // Invalid email format + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + firstName: invalidFirstName + }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0211'); + }); + }); + + /** SUBSCRIPTIONS **/ + + describe('GET /users', () => { + + it('should check if a user is subscribed to a specific course and return true', async () => { + const courseId = '651d3a15cda7d5bd2878dfc7'; + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // Find the user and update their subscriptions + const result = await db.collection('users').findOneAndUpdate( + { _id: userId }, // Convert userId to ObjectId if needed + { $push: { subscriptions: courseId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + + const updatedUser = result.value; + + // Check if the subscription was successfully added + expect(updatedUser.subscriptions.includes(courseId)).toBe(true); + + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + + expect(response.status).toBe(200); + expect(response.text).toBe('true'); + }); + + + + it('should return false if a user is not subscribed to a specific course', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + + expect(response.status).toBe(200); + expect(response.text).toBe('false'); + + }); + }); + + describe('GET /users/:id/subscriptions', () => { + it('should get user subscriptions', async () => { + + const courseId = '651d3a15cda7d5bd2878dfc7'; + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // Find the user and update their subscriptions + const result = await db.collection('users').findOneAndUpdate( + { _id: userId }, // Convert userId to ObjectId if needed + { $push: { subscriptions: courseId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + + const updatedUser = result.value; + + // Check if the subscription was successfully added + expect(updatedUser.subscriptions.includes(courseId)).toBe(true); + + const response = await request(`http://localhost:${PORT}`) + .get('/api/users/' + userId + '/subscriptions'); + + expect(response.status).toBe(200); + expect(response.body).toBeInstanceOf(Array); + expect(response.body.find((element) => element == courseId)); + + }); + }); + + afterAll(async () => { + db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + server.close(); + await mongoose.connection.close(); + }); + }); \ No newline at end of file diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index a2af268b..f8932a67 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:64546/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:65468/"} \ No newline at end of file diff --git a/applications/content-creator-applications/controller/contentCreatorApplicationControllerspec.js b/applications/content-creator-applications/controller/contentCreatorApplicationController_test.js similarity index 99% rename from applications/content-creator-applications/controller/contentCreatorApplicationControllerspec.js rename to applications/content-creator-applications/controller/contentCreatorApplicationController_test.js index 0e1c34c9..1d8bda5d 100644 --- a/applications/content-creator-applications/controller/contentCreatorApplicationControllerspec.js +++ b/applications/content-creator-applications/controller/contentCreatorApplicationController_test.js @@ -1,3 +1,4 @@ + const connectDb = require('../../../__tests__/fixtures/db'); const makeFakeContentCreatorApplication = require('../../../__tests__/fixtures/fakeContentCreatorApplication'); @@ -28,6 +29,8 @@ describe('Content Creator Application Controller', () => { expect(response.success).toBe(true); }); + + it('gets a specific content creator application', async () => { const fakeApplication = makeFakeContentCreatorApplication(); @@ -78,4 +81,4 @@ describe('Content Creator Application Controller', () => { await mongoose.connection.close(); }); -}); \ No newline at end of file +}); diff --git a/applications/content-creator-applications/domain/contentCreatorApplication.spec.js b/applications/content-creator-applications/domain/notInUseTest_contentCreatorApplication.js similarity index 100% rename from applications/content-creator-applications/domain/contentCreatorApplication.spec.js rename to applications/content-creator-applications/domain/notInUseTest_contentCreatorApplication.js diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index c7ef5e24..4989e4dd 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -479,7 +479,7 @@ router.post("/user/", async (req, res) => { //Get all courses -router.get("/courses", async (req, res) => { +router.get('', async (req, res) => { try { // find all courses in the database @@ -495,7 +495,7 @@ router.get("/courses", async (req, res) => { // Get specific course -router.get("/courses/:id", async (req, res) => { +router.get('/:id', async (req, res) => { try { const { id } = req.params; @@ -513,7 +513,7 @@ router.get("/courses/:id", async (req, res) => { // Get all sections from course -router.get("/courses/:id/sections", async (req, res) => { +router.get('/:id/sections', async (req, res) => { try { const { id } = req.params; @@ -531,7 +531,7 @@ router.get("/courses/:id/sections", async (req, res) => { }); // Get a specififc section -router.get("/courses/:courseId/sections/:sectionId", async (req, res) => { +router.get('/:courseId/sections/:sectionId', async (req, res) => { try { const { courseId, sectionId } = req.params; @@ -549,7 +549,7 @@ router.get("/courses/:courseId/sections/:sectionId", async (req, res) => { /* // Get all excercies from a section *** commented out since we do not use it per 10/10 -router.get("/courses/:courseId/sections/:sectionId/exercises", async (req, res) => { +router.get("/:courseId/sections/:sectionId/exercises", async (req, res) => { try { const { courseId, sectionId } = req.params; @@ -569,7 +569,7 @@ router.get("/courses/:courseId/sections/:sectionId/exercises", async (req, res) /*** SUBSCRIPTION ROUTES ***/ // Subscribe to course -router.post("/courses/:id/subscribe", async (req, res) => { +router.post('/:id/subscribe', async (req, res) => { try { const { id } = req.params; @@ -595,7 +595,7 @@ router.post("/courses/:id/subscribe", async (req, res) => { }); // Unsubscribe to course -router.post("/courses/:id/unsubscribe", async (req, res) => { +router.post('/:id/unsubscribe', async (req, res) => { try { const { id } = req.params; @@ -618,56 +618,6 @@ router.post("/courses/:id/unsubscribe", async (req, res) => { } }); - -// Get users subscriptions -router.get("/users/:id/subscriptions", async (req, res) => { - try { - const userId = req.params.id; - - // Find the user by _id and select the 'subscriptions' field - const user = await User.findById(userId).select('subscriptions'); - - if (!user) { - return res.status(404).json({ message: 'User not found' }); - } - - const subscribedCourses = user.subscriptions; - - // Find courses based on the subscribed course IDs - const list = await CourseModel.find({ '_id': { $in: subscribedCourses } }); - - console.log(userId); - console.log(list); - res.send(list); - - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } -}); - - - -// Checks if user is subscribed to a specific course -router.get('/users', async (req, res) => { - try { - const { user_id, course_id } = req.query; - - // Check if the course_id exists in the user's subscriptions array - const userTest = await User.findById(user_id); - - if (userTest.subscriptions.includes(course_id)) { - // User is subscribed to the course - res.send("true"); - } else { - // User is not subscribed to the course - res.send("false"); - } - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } - }); module.exports = router; diff --git a/routes/index.js b/routes/index.js index edf26d22..8779e24c 100644 --- a/routes/index.js +++ b/routes/index.js @@ -16,7 +16,7 @@ router.get('/api', (req, res) => { res.send(router.stack); }); -router.use('/api', CourseRoutes); +router.use('/api/courses', CourseRoutes); router.use('', AWSRoutes); router.use('/api', AuthRoutes); router.use('/api/signup', SignupRoutes); diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 4554311f..e14f3729 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -2,6 +2,7 @@ const router = require('express').Router(); const { validateEmail, validateName } = require('../helpers/validation'); const errorCodes = require('../helpers/errorCodes'); const { User } = require('../models/User'); +const { CourseModel } = require('../models/Courses'); const requireLogin = require('../middlewares/requireLogin'); router.delete('/delete/:id', requireLogin, async (req, res) => { @@ -88,6 +89,56 @@ async function validateFields(fields) { return true; } +// Get users subscriptions +router.get('/:id/subscriptions', async (req, res) => { + try { + const userId = req.params.id; + + // Find the user by _id and select the 'subscriptions' field + const user = await User.findById(userId).select('subscriptions'); + + if (!user) { + return res.status(404).json({ message: 'User not found' }); + } + + const subscribedCourses = user.subscriptions; + + // Find courses based on the subscribed course IDs + const list = await CourseModel.find({ '_id': { $in: subscribedCourses } }); + + console.log(userId); + console.log(list); + res.send(list); + + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } +}); + + + +// Checks if user is subscribed to a specific course +router.get('', async (req, res) => { + try { + const { user_id, course_id } = req.query; + + // Check if the course_id exists in the user's subscriptions array + const userTest = await User.findById(user_id); + + if (userTest.subscriptions.includes(course_id)) { + // User is subscribed to the course + res.send("true"); + } else { + // User is not subscribed to the course + res.send("false"); + } + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } + }); + module.exports = router; \ No newline at end of file From bfe7e4b1013d4550c964f1339a832670da033d05 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 13:25:23 +0200 Subject: [PATCH 092/129] test: Tests added for email helper and authRoutes Changes mads: - Added tests for different outcomes of password reset request - Added tests for different outcomes of password reset - Added documentation/comments for routes and functions added in last commit - Removed helpers from ignore patterns in package.json - Tested sendResetPasswordEmail and sendMail from email test - Removed leftover console.logs --- __tests__/helpers/email.test.js | 78 +++++++++ __tests__/middleware/adminOnly.test.js | 4 +- __tests__/middleware/requireLogin.spec.js | 4 +- __tests__/routes/authRoutes.spec.js | 109 ------------ __tests__/routes/authRoutes.test.js | 192 ++++++++++++++++++++++ __tests__/routes/courseRoutes.test.js | 4 +- __tests__/routes/signupRoutes.spec.js | 4 +- helpers/email.js | 127 ++++++++------ middlewares/adminOnly.js | 1 - middlewares/requireLogin.js | 1 - package.json | 4 +- routes/authRoutes.js | 37 ++++- routes/signupRoutes.js | 1 - routes/testRoutes.js | 1 - 14 files changed, 384 insertions(+), 183 deletions(-) create mode 100644 __tests__/helpers/email.test.js delete mode 100644 __tests__/routes/authRoutes.spec.js create mode 100644 __tests__/routes/authRoutes.test.js diff --git a/__tests__/helpers/email.test.js b/__tests__/helpers/email.test.js new file mode 100644 index 00000000..008e9f2c --- /dev/null +++ b/__tests__/helpers/email.test.js @@ -0,0 +1,78 @@ +const exp = require('constants'); +const emailHelper = require('../../helpers/email'); +const nodemailer = require('nodemailer'); + +jest.mock('nodemailer', () => { + return { + createTransport: jest.fn(() => { + return { + sendMail: jest.fn(), + }; + }), + }; +}); + +jest.mock('../../config/keys', () => { + return { + GMAIL_USER: 'educadotest4@gmail.com', + GMAIL_APP_PASSWORD: 'test', + }; +}); + +describe('sendResetPasswordEmail', () => { + + it('should return email if email is sent', async () => { + const user = { + firstName: 'John', + email: 'test@email.com', + } + const token = '1234'; + + const expectedMailOptions = { + subject: 'Reset password request for Educado', + from: 'educadotest4@gmail.com', + to: user.email, + text: `Hi ${user.firstName},\n\nYou have requested to reset your password. Please enter the following code in the app to reset your password:\n\n${token}` + + `\n\nThis code will expire in 5 minutes.\n\nIf you did not request to reset your password, please ignore this email. Your password will remain unchanged.` + + `\n\nBest regards,\nThe Educado team`, + html: `

Hi ${user.firstName},

\n` + + `

You have requested to reset your password. Please enter the following code in the app to reset your password:

\n` + + `

${token}

\n` + + `

This code will expire in 5 minutes.

\n` + + `

If you did not request this, please ignore this email and your password will remain unchanged.

\n` + + `

Best regards,

\n` + + `

The Educado team

` + }; + + const result = await emailHelper.sendResetPasswordEmail(user, token); + expect(result).toMatchObject(expectedMailOptions); + }); +}); + +describe('sendMail', () => { + + it('should return email if email is sent', async () => { + const mailOptions = { + subject: 'Test email', + from: 'educadotest4@gmail.com', + to: 'test@user.com', + text: 'This is a test email', + html: '

This is a test email

' + }; + + const result = await emailHelper.sendMail(mailOptions); + expect(result).toMatchObject(mailOptions); + }); + + it('should throw error if email is invalid', async () => { + const mailOptions = { + subject: 'Test email', + from: 'invalid', + to: '', + text: 'This is a test email', + html: '

This is a test email

' + }; + + await expect(emailHelper.sendMail(mailOptions)).rejects.toThrow('Invalid email'); + }); +}); \ No newline at end of file diff --git a/__tests__/middleware/adminOnly.test.js b/__tests__/middleware/adminOnly.test.js index 567675fd..4809eb01 100644 --- a/__tests__/middleware/adminOnly.test.js +++ b/__tests__/middleware/adminOnly.test.js @@ -11,9 +11,7 @@ app.use('/api', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5022; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Mocked token secret const TOKEN_SECRET = 'test'; diff --git a/__tests__/middleware/requireLogin.spec.js b/__tests__/middleware/requireLogin.spec.js index d52e910e..b23eb508 100644 --- a/__tests__/middleware/requireLogin.spec.js +++ b/__tests__/middleware/requireLogin.spec.js @@ -11,9 +11,7 @@ app.use('/api/test', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5022; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Mocked token secret const TOKEN_SECRET = 'test'; diff --git a/__tests__/routes/authRoutes.spec.js b/__tests__/routes/authRoutes.spec.js deleted file mode 100644 index 6928a408..00000000 --- a/__tests__/routes/authRoutes.spec.js +++ /dev/null @@ -1,109 +0,0 @@ -const request = require('supertest'); -const express = require('express'); -const router = require('../../routes/authRoutes'); // Import your router file here -const connectDb = require('../../__tests__/fixtures/db'); -const makeFakeUser = require('../../__tests__/fixtures/fakeUser'); -const mongoose = require('mongoose'); -const { encrypt } = require('../../helpers/password'); - -const app = express(); -app.use(express.json()); -app.use('/api', router); // Mount the router under '/api' path - -// Mock Google OAuth2 clientID -jest.mock('../../config/keys', () => { - return { - GOOGLE_CLIENT_ID: 'test', - TOKEN_SECRET: 'test', - }; -}); - -// Start the Express app on a specific port for testing -const PORT = 5020; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); - -const fakeUser = makeFakeUser(); - -describe('Login User route', () => { - - let db; // Store the database connection - - beforeAll(async () => { - db = await connectDb(); // Connect to the database - - // Insert the fake user into the database - await db.collection('users').insertOne(fakeUser); - }); - - it('Returns error if user is not found', async () => { - const nonExistingUser = { - email: 'iDontExist@test.dk', - password: encrypt('12345678') - }; - - // Send a Post request to the login endpoint - const response = await request(`http://localhost:${PORT}`) - .post('/api/auth/login') - .send(nonExistingUser) - .expect(401); - - // Verify the response body - expect(response.body.error.code).toBe('E0101'); - }); - - it('Returns error if user is not found', async () => { - const nonExistingUser = { - email: 'iDontExist@test.dk', - password: encrypt('12345678') - }; - - // Send a Post request to the login endpoint - const response = await request(`http://localhost:${PORT}`) - .post('/api/auth/login') - .send(nonExistingUser) - .expect(401); - - // Verify the response body - expect(response.body.error.code).toBe('E0101'); - }); - - - it('Returns error if password is incorrect', async () => { - const incorrectPassword = { - email: fakeUser.email, - password: 'incorrectPassword' - }; - - const response = await request(`http://localhost:${PORT}`) - .post('/api/auth/login') - .send(incorrectPassword) - .expect(401); - - // Verify the response body - expect(response.body.error.code).toBe('E0105'); - }); - - it('Returns token if user is found and password is correct', async () => { - const correctCredentials = { - email: fakeUser.email, - password: 'ABC123456!' - }; - - const response = await request(`http://localhost:${PORT}`) - .post('/api/auth/login') - .send(correctCredentials) - .expect(202); - - // Verify the response body - expect(response.body.status).toBe('login successful'); - expect(response.body.accessToken).toBeDefined(); - }); - - afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection - server.close(); - await mongoose.connection.close(); - }); -}); \ No newline at end of file diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js new file mode 100644 index 00000000..40d9a60a --- /dev/null +++ b/__tests__/routes/authRoutes.test.js @@ -0,0 +1,192 @@ +const request = require('supertest'); +const express = require('express'); +const { router, generatePasswordResetToken, getRandomNumber } = require('../../routes/authRoutes'); // Import your router file here +const connectDb = require('../fixtures/db'); +const makeFakeUser = require('../fixtures/fakeUser'); +const mongoose = require('mongoose'); +const { encrypt } = require('../../helpers/password'); +const { sendResetPasswordEmail } = require('../../helpers/email'); +const exp = require('constants'); + + +const app = express(); +app.use(express.json()); +app.use('/api/auth', router); // Mount the router under '/api' path + +// Mock Google OAuth2 clientID +jest.mock('../../config/keys', () => { + return { + GOOGLE_CLIENT_ID: 'test', + TOKEN_SECRET: 'test', + }; +}); + +jest.mock('../../helpers/email', () => { + return { + sendResetPasswordEmail: jest.fn(), + }; +}); + +// Start the Express app on a specific port for testing +const PORT = 5020; // Choose a port for testing +const server = app.listen(PORT); + +const fakeUser = makeFakeUser(); +let db; // Store the database connection +beforeAll(async () => { + db = await connectDb(); // Connect to the database +}); + +describe('Login User route', () => { + + beforeAll(async () => { + // Insert the fake user into the database + await db.collection('users').insertOne(fakeUser); + }); + + it('Returns error if user is not found', async () => { + const nonExistingUser = { + email: 'iDontExist@test.dk', + password: encrypt('12345678') + }; + + // Send a Post request to the login endpoint + const response = await request(`http://localhost:${PORT}`) + .post('/api/auth/login') + .send(nonExistingUser) + .expect(401); + + // Verify the response body + expect(response.body.error.code).toBe('E0101'); + }); + + it('Returns error if user is not found', async () => { + const nonExistingUser = { + email: 'iDontExist@test.dk', + password: encrypt('12345678') + }; + + // Send a Post request to the login endpoint + const response = await request(`http://localhost:${PORT}`) + .post('/api/auth/login') + .send(nonExistingUser) + .expect(401); + + // Verify the response body + expect(response.body.error.code).toBe('E0101'); + }); + + + it('Returns error if password is incorrect', async () => { + const incorrectPassword = { + email: fakeUser.email, + password: 'incorrectPassword' + }; + + const response = await request(`http://localhost:${PORT}`) + .post('/api/auth/login') + .send(incorrectPassword) + .expect(401); + + // Verify the response body + expect(response.body.error.code).toBe('E0105'); + }); + + it('Returns token if user is found and password is correct', async () => { + const correctCredentials = { + email: fakeUser.email, + password: 'ABC123456!' + }; + + const response = await request(`http://localhost:${PORT}`) + .post('/api/auth/login') + .send(correctCredentials) + .expect(202); + + // Verify the response body + expect(response.body.status).toBe('login successful'); + expect(response.body.accessToken).toBeDefined(); + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + }); +}); + +describe('generatePasswordResetToken', () => { + it('Returns a string of four digits', async () => { + const token = generatePasswordResetToken(); + expect(typeof token).toBe('string'); + expect(token.length).toBe(4); + expect(Number.isInteger(Number(token))).toBe(true); + }); +}); + +describe('getRandomNumber', () => { + it('Returns a number between min and max', async () => { + const min = 0; + const max = 9; + const number = getRandomNumber(min, max); + expect(typeof number).toBe('number'); + expect(number).toBeGreaterThanOrEqual(min); + expect(number).toBeLessThanOrEqual(max); + }); +}); + +describe('Reset password request route', () => { + + beforeAll(async () => { + // Insert the fake user into the database + await db.collection('users').insertOne(fakeUser); + }); + + it('Returns error if email is not found', async () => { + const nonExistingEmail = { email: 'test@email.com' } + const res = await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-request') + .send(nonExistingEmail) + .expect(400); + + expect(res.body.error.code).toBe('E0401'); + }); + + it('Returns success if email is found and email is sent', async () => { + sendResetPasswordEmail.mockImplementation(() => true); + /*jest.mock('../../helpers/email', () => { + return { + sendResetPasswordEmail: jest.fn((email, token) => { + return true; + }), + }; + });*/ + const existingEmail = { email: fakeUser.email } + const res = await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-request') + .send(existingEmail) + .expect(200); + + expect(res.body.status).toBe('success'); + }); + + it('Returns error if email is not succesfully sent', async () => { + sendResetPasswordEmail.mockImplementation(() => false); + const existingEmail = { email: fakeUser.email } + const res = await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-request') + .send(existingEmail) + .expect(500); + expect(res.body.error.code).toBe('E0004'); + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + }); +}); + +describe('Reset password route', () => { +}); + +afterAll(async () => { + await mongoose.connection.close(); + server.close(); +}); \ No newline at end of file diff --git a/__tests__/routes/courseRoutes.test.js b/__tests__/routes/courseRoutes.test.js index 11372fe9..f6e244c4 100644 --- a/__tests__/routes/courseRoutes.test.js +++ b/__tests__/routes/courseRoutes.test.js @@ -23,9 +23,7 @@ jest.mock('../../config/keys', () => { // Start the Express app on a specific port for testing const PORT = 5022; // Choose a port for testing const ADMIN_ID = 'srdfet784y2uioejqr'; -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); const fakeUser = makeFakeUser(); const fakeCourses = getFakeCourses(); diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 65c27227..2adaffa5 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -12,9 +12,7 @@ app.use('/api/signup', router); // Mount the router under '/api/signup' path // Start the Express app on a specific port for testing const PORT = 5021; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Create a fake user let fakeUser = makeFakeUser(); diff --git a/helpers/email.js b/helpers/email.js index cfccf2f2..3f10c8e8 100644 --- a/helpers/email.js +++ b/helpers/email.js @@ -1,68 +1,93 @@ const keys = require('../config/keys'); const nodemailer = require('nodemailer'); -const regexPatterns = require('./patterns'); +const { patterns } = require('./patterns'); module.exports = Object.freeze({ - isValid, - sendResetPasswordEmail, + sendResetPasswordEmail, + sendMail }); -function isValid(email) { - return regexPatterns.email.test(email); -} - - +/** + * Function to send an email using nodemailer. + * @param {Object} mail information about the mail to be sent, containing the following properties: + * - subject: the subject of the email + * - from: the sender of the email + * - to: the recipient of the email + * - text: the text of the email + * - html: the html of the email + * @param {String} mail.subject + * @param {String} mail.from + * @param {String} mail.to + * @param {String} mail.text + * @param {String} mail.html + */ async function sendMail({ - subject, - from = keys.gmailUser, - to, - text, - html + subject, + from = keys.GMAIL_USER, + to, + text, + html }) { - const transporter = nodemailer.createTransport({ - service: 'gmail', - auth: { - type: 'login', - user: keys.GMAIL_USER, - pass: keys.GMAIL_APP_PASSWORD, + if (!patterns.email.test(from) || !patterns.email.test(to)) { + throw new Error('Invalid email'); + } + + const transporter = nodemailer.createTransport({ + service: 'gmail', // Send mail using gmail + auth: { // Authenticate the gmail account (sender) + type: 'login', + user: keys.GMAIL_USER, + pass: keys.GMAIL_APP_PASSWORD, - }, - tls: { - rejectUnauthorized: false, - } - }); + }, + tls: { + rejectUnauthorized: false, // Disable TLS/SSL certificate validation + } + }); - const mailOptions = { - subject: subject, - from: from, - to: to, - text: text, - html: html - }; + const mailOptions = { + subject: subject, + from: from, + to: to, + text: text, + html: html + }; - await transporter.sendMail(mailOptions); + await transporter.sendMail(mailOptions); + return mailOptions; } -function sendResetPasswordEmail(user, token) { + +/** + * + * @param {Object} user - containing the following properties: + * - firstName: the first name of the user + * - email: the email of the user + * @param {String} user.firstName + * @param {String} user.email + * @param {PasswordResetToken} token + * @returns + */ +async function sendResetPasswordEmail(user, token) { const subject = 'Reset password request for Educado'; const to = user.email; - const text = `Hi ${user.firstName}, - You have requested to reset your password. Please enter the following code in the app to reset your password: - - ${token} + const text = `Hi ${user.firstName},\n\nYou have requested to reset your password. Please enter the following code in the app to reset your password:\n\n${token}`+ + `\n\nThis code will expire in 5 minutes.\n\nIf you did not request to reset your password, please ignore this email. Your password will remain unchanged.` + + `\n\nBest regards,\nThe Educado team`; + const html = `

Hi ${user.firstName},

\n`+ + `

You have requested to reset your password. Please enter the following code in the app to reset your password:

\n` + + `

${token}

\n`+ + `

This code will expire in 5 minutes.

\n`+ + `

If you did not request this, please ignore this email and your password will remain unchanged.

\n`+ + `

Best regards,

\n` + + `

The Educado team

`; - If you did not request this, please ignore this email and your password will remain unchanged. - - Best regards, - The Educado team - `; - const html = ` -

Hi ${user.firstName},

-

You have requested to reset your password. Please enter the following code in the app to reset your password:

-

${token}

-

If you did not request this, please ignore this email and your password will remain unchanged.

- `; - sendMail({ subject, to, text, html }); - return true; -} + try { + const mail = await sendMail({ subject, to, text, html }); + return mail; + } catch (e) { + console.log(e); + throw e; + } +} \ No newline at end of file diff --git a/middlewares/adminOnly.js b/middlewares/adminOnly.js index 8d3eca80..fe5dac39 100644 --- a/middlewares/adminOnly.js +++ b/middlewares/adminOnly.js @@ -3,7 +3,6 @@ const { verify } = require('../helpers/token'); const ADMIN_ID = 'srdfet784y2uioejqr' module.exports = (req, res, next) => { - console.log("Params: ", req.params); try { const claims = verify(req.headers.token ?? ''); if(claims.id !== ADMIN_ID) { diff --git a/middlewares/requireLogin.js b/middlewares/requireLogin.js index f3f5c261..7d8969dd 100644 --- a/middlewares/requireLogin.js +++ b/middlewares/requireLogin.js @@ -1,7 +1,6 @@ const { verify } = require('../helpers/token'); module.exports = (req, res, next) => { - console.log("Params: ", req.params); try { const claims = verify(req.headers.token ?? ''); if(req.params.id != null) { diff --git a/package.json b/package.json index 9c4011c2..0ff3ac63 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "verbose": true, "modulePathIgnorePatterns": [ "/__tests__/fixtures", - "/__tests__/helpers", - "/__tests__/setup" + "/__tests__/setup", + "/__tests__/helpers/timeout.js" ], "globalSetup": "/__tests__/setup/jest-setup.js", "globalTeardown": "/__tests__/setup/jest-teardown.js", diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 32784111..3ae527fd 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -10,6 +10,8 @@ const send = require('send'); const { sendResetPasswordEmail } = require('../helpers/email'); const { PasswordResetToken } = require('../models/PasswordResetToken'); +const EXPIRATION_TIME = 1000 * 60 * 5; + // Services //require("../services/passport"); @@ -37,7 +39,6 @@ router.get("/auth/google/callback", // Login router.post('/login', async (req, res) => { try { - console.log(req.body); // Searching for a single user in the database, with the email provided in the request body const user = await User.findOne({ email: req.body.email}); // If email is found, compare the password provided in the request body with the password in the database @@ -76,23 +77,31 @@ router.post('/login', async (req, res) => { router.post('/reset-password-request', async (req, res) => { const { email } = req.body; const user = await User.findOne({ email: email }); + + // If email is not provided or user is not found, return error E0401 if (!email || !user) { return res.status(400).json({ error: errorCodes['E0401'] }); } + // Delete any existing token let token = await PasswordResetToken.findOne({ userId: user._id }); if (token) await token.deleteOne(); + + // Generate new token let resetToken = generatePasswordResetToken(); - console.log(resetToken) const hash = await encrypt(resetToken); + // Save token to database with 5 minute expiration await new PasswordResetToken({ userId: user._id, token: hash, - expiresAt: Date.now() + 1000 * 60 * 5 // 2 minutes + expiresAt: Date.now() + EXPIRATION_TIME // 5 minutes }).save(); + // Send email with reset token const success = await sendResetPasswordEmail(user, resetToken); + + // Return success if email is sent, else return error code E0004 if(success) { return res.status(200).json({ status: 'success' }); } else { @@ -104,22 +113,28 @@ router.put('/reset-password', async (req, res) => { const { email, token, newPassword } = req.body; const user = await User.findOne({ email: email }); - if (!email || !user) { + if (!email || !user) { // If email is not provided or user is not found, return error E0401 return res.status(400).json({ error: errorCodes['E0401'] }); } const passwordResetToken = await PasswordResetToken.findOne({ userId: user._id}); + + // If token is not provided or token is expired, return error E0404 if (!passwordResetToken || passwordResetToken.expiresAt < Date.now()) { return res.status(400).json({ error: errorCodes['E0404'] }); } const isValid = await compare(token, passwordResetToken.token); + + // If token is invalid, return error E0405 if (!isValid) { return res.status(400).json({ error: errorCodes['E0405'] }); } + // Update password and delete token user.password = await encrypt(newPassword); await User.updateOne({ _id: user._id }, user); await passwordResetToken.deleteOne(); + // Return success return res.status(200).json({ status: 'success' }); }); @@ -139,6 +154,10 @@ router.get('/current_user', (req, res) => { module.exports = router; +/** + * Generates a random 4 digit code for password reset + * @returns {String} - 4 digit code as a string + */ function generatePasswordResetToken() { const length = 4; let retVal = ''; @@ -148,8 +167,16 @@ function generatePasswordResetToken() { return retVal; } +/** + * Generates a random number between min and max + * @param {Number} min - Minimum number + * @param {Number} max - Maximum number + * @returns {Number} Random number between min and max + */ function getRandomNumber(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; -} \ No newline at end of file +} + +module.exports = { generatePasswordResetToken, getRandomNumber, router }; // Export the functions for testing \ No newline at end of file diff --git a/routes/signupRoutes.js b/routes/signupRoutes.js index e3bfab5e..9ef354a8 100644 --- a/routes/signupRoutes.js +++ b/routes/signupRoutes.js @@ -50,7 +50,6 @@ router.post('/user', async (req, res) => { } } catch (error) { - console.log(error); res.status(400); res.send({ error: error diff --git a/routes/testRoutes.js b/routes/testRoutes.js index 2e3d806c..ce8999b7 100644 --- a/routes/testRoutes.js +++ b/routes/testRoutes.js @@ -4,7 +4,6 @@ const requireLogin = require('../middlewares/requireLogin'); // Route for testing JWT verification on private routes router.get('/require-jwt', requireLogin, (req, res) => { - console.log(req.headers.token); // Mirror the input for testing res.status(200).send(req.body); }); From 4c6a807d10ca13cc273ec26494a0a2298744a0b4 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 13:27:53 +0200 Subject: [PATCH 093/129] renamed to avoid unused tests to fail --- ...est.js => notInUseTest_contentCreatorApplicationController.js} | 0 ...nListspec.js => notInUseTest_contentCreatorApplicationList.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename applications/content-creator-applications/controller/{contentCreatorApplicationController_test.js => notInUseTest_contentCreatorApplicationController.js} (100%) rename applications/content-creator-applications/data-access/{contentCreatorApplicationListspec.js => notInUseTest_contentCreatorApplicationList.js} (100%) diff --git a/applications/content-creator-applications/controller/contentCreatorApplicationController_test.js b/applications/content-creator-applications/controller/notInUseTest_contentCreatorApplicationController.js similarity index 100% rename from applications/content-creator-applications/controller/contentCreatorApplicationController_test.js rename to applications/content-creator-applications/controller/notInUseTest_contentCreatorApplicationController.js diff --git a/applications/content-creator-applications/data-access/contentCreatorApplicationListspec.js b/applications/content-creator-applications/data-access/notInUseTest_contentCreatorApplicationList.js similarity index 100% rename from applications/content-creator-applications/data-access/contentCreatorApplicationListspec.js rename to applications/content-creator-applications/data-access/notInUseTest_contentCreatorApplicationList.js From 9bc3d6f84d11a4e9c071d037d4442668e82d3d68 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 13:28:56 +0200 Subject: [PATCH 094/129] format fix --- __tests__/routes/courseRoutes.spec.js | 60 +++++++++++++-------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/__tests__/routes/courseRoutes.spec.js b/__tests__/routes/courseRoutes.spec.js index eb6fc87f..c4cb5338 100644 --- a/__tests__/routes/courseRoutes.spec.js +++ b/__tests__/routes/courseRoutes.spec.js @@ -1,7 +1,7 @@ const request = require('supertest'); const express = require('express'); -const router = require('../../routes/courseRoutes'); -const mongoose = require('mongoose'); +const router = require('../../routes/courseRoutes'); +const mongoose = require('mongoose'); const connectDb = require('../../__tests__/fixtures/db'); const makeFakeUser = require('../fixtures/fakeUser'); const makeFakeCourse = require('../fixtures/fakeCourse'); @@ -18,7 +18,7 @@ app.use('/api/courses', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5021; // Choose a port for testing const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); + console.log(`Express server is running on port ${PORT}`); }); // Create a fake user, course and section @@ -30,35 +30,35 @@ describe('Course Routes', () => { let db; // Store the database connection - beforeAll(async () => { - db = await connectDb(); // Connect to the database + beforeAll(async () => { + db = await connectDb(); // Connect to the database - // Insert the fake user, courses and sections into the database - await db.collection('users').insertOne(fakeUser); + // Insert the fake user, courses and sections into the database + await db.collection('users').insertOne(fakeUser); await db.collection('courses').insertOne(fakeCourse); await db.collection('sections').insertOne(fakeSection); - }); + }); describe('GET /courses', () => { it('should get all courses', async () => { const response = await request(`http://localhost:${PORT}`) - .get('/api/courses'); + .get('/api/courses'); expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Array); - + }); }); describe('GET /courses/:id', () => { it('should get a specific course', async () => { - const course = await db.collection('courses').findOne({ title: 'test course'}); + const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id - const response = await request(`http://localhost:${PORT}`) - .get('/api/courses/' + courseId) + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId) expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); @@ -69,11 +69,11 @@ describe('Course Routes', () => { describe('GET /courses/:id/sections', () => { it('should get all sections from a course', async () => { - const course = await db.collection('courses').findOne({ title: 'test course'}); + const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id; const response = await request(`http://localhost:${PORT}`) - .get('/api/courses/' + courseId + '/sections'); + .get('/api/courses/' + courseId + '/sections'); expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Array); @@ -84,14 +84,14 @@ describe('Course Routes', () => { describe('GET /courses/:courseId/sections/:sectionId', () => { it('should get a specific section', async () => { - const course = await db.collection('courses').findOne({ title: 'test course'}); + const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id; - const section = await db.collection('sections').findOne({title: 'test section'}); + const section = await db.collection('sections').findOne({ title: 'test section' }); const sectionId = section._id; const response = await request(`http://localhost:${PORT}`) - .get('/api/courses/' + courseId + '/sections/' + sectionId); + .get('/api/courses/' + courseId + '/sections/' + sectionId); expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); @@ -102,15 +102,15 @@ describe('Course Routes', () => { describe('POST /courses/:id/subscribe', () => { it('should subscribe a user to a course', async () => { - const course = await db.collection('courses').findOne({ title: 'test course'}); + const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id; - const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); const userId = user._id; const response = await request(`http://localhost:${PORT}`) .post('/api/courses/' + courseId + '/subscribe') - .send({ user_id: userId}); + .send({ user_id: userId }); console.log("course id: " + courseId) console.log('response.body.subscriptions:', response.body.subscriptions) @@ -125,10 +125,10 @@ describe('Course Routes', () => { describe('POST /courses/:id/unsubscribe', () => { it('should unsubscribe a user from a course', async () => { - const course = await db.collection('courses').findOne({ title: 'test course'}); + const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id; - const user = await db.collection('users').findOne({ email: 'fake@gmail.com'}); + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); const userId = user._id; const response = await request(`http://localhost:${PORT}`) @@ -143,12 +143,12 @@ describe('Course Routes', () => { }); -afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection - db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection - db.collection('sections').deleteMany({}); // Delete all documents in the 'sections' collection - server.close(); - await mongoose.connection.close(); -}); + afterAll(async () => { + db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + db.collection('sections').deleteMany({}); // Delete all documents in the 'sections' collection + server.close(); + await mongoose.connection.close(); + }); }); From a6c681a3edb9f5c58c4e9a25e111ea23ee9e5cf6 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 13:43:22 +0200 Subject: [PATCH 095/129] added specific error messages --- routes/courseRoutes.js | 10 +++++----- routes/userRoutes.js | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 4989e4dd..a69afb7d 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -505,7 +505,7 @@ router.get('/:id', async (req, res) => { res.send(course); } catch (error) { - console.error(error); + console.error("Error getting course based on course id, message: " + error); res.status(500).json({ message: 'Server error' }); } @@ -524,7 +524,7 @@ router.get('/:id/sections', async (req, res) => { res.send(sections); } catch (error) { - console.error(error); + console.error("Error getting sections for course based on course id, message: " + error); res.status(500).json({ message: 'Server error' }); } @@ -541,7 +541,7 @@ router.get('/:courseId/sections/:sectionId', async (req, res) => { res.send(section); } catch (error) { - console.error(error); + console.error("Error getting specific section based on section id, message: " + error); res.status(500).json({ message: 'Server error' }); } @@ -588,7 +588,7 @@ router.post('/:id/subscribe', async (req, res) => { res.send(user); } catch (error) { - console.error(error); + console.error("Error getting user to subscribe to course, message: " + error); res.status(500).json({ message: 'Server error' }); } @@ -613,7 +613,7 @@ router.post('/:id/unsubscribe', async (req, res) => { res.send(user) } catch (error) { - console.error(error); + console.error(("Error getting user to unsubscribe, message: " + error)); res.status(500).json({ message: 'Server error' }); } diff --git a/routes/userRoutes.js b/routes/userRoutes.js index e14f3729..62884c50 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -89,6 +89,8 @@ async function validateFields(fields) { return true; } +/** SUBSCRIPTIONS **/ + // Get users subscriptions router.get('/:id/subscriptions', async (req, res) => { try { @@ -111,7 +113,7 @@ router.get('/:id/subscriptions', async (req, res) => { res.send(list); } catch (error) { - console.error(error); + console.error(("Error getting users subscribed courses, message: " + error)); res.status(500).json({ message: 'Server error' }); } }); @@ -134,7 +136,7 @@ router.get('', async (req, res) => { res.send("false"); } } catch (error) { - console.error(error); + console.error(("Error checking if user is subscribed to course, message: " + error)); res.status(500).json({ message: 'Server error' }); } }); From 51005cd49820eb8dfd58f030a33d18345f0782cb Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 13:52:14 +0200 Subject: [PATCH 096/129] removed console log --- routes/userRoutes.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 62884c50..e787cc4a 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -108,8 +108,6 @@ router.get('/:id/subscriptions', async (req, res) => { // Find courses based on the subscribed course IDs const list = await CourseModel.find({ '_id': { $in: subscribedCourses } }); - console.log(userId); - console.log(list); res.send(list); } catch (error) { From ab95d1578137c743a8ad8a6e4ff2b5bfda995df4 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 13:56:02 +0200 Subject: [PATCH 097/129] removed idea and fixed 404 to 204 --- .idea/.gitignore | 3 -- .idea/educado-backend.iml | 9 ------ .idea/misc.xml | 6 ---- .idea/modules.xml | 8 ----- .idea/vcs.xml | 6 ---- .idea/workspace.xml | 44 -------------------------- __tests__/setup/globalConfigMongo.json | 2 +- routes/userRoutes.js | 2 +- 8 files changed, 2 insertions(+), 78 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/educado-backend.iml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/educado-backend.iml b/.idea/educado-backend.iml deleted file mode 100644 index d6ebd480..00000000 --- a/.idea/educado-backend.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 639900d1..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index fe7a66f7..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddf..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 502532f6..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - 1695030594010 - - - - \ No newline at end of file diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index f8932a67..1cbe8e46 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:65468/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:49426/"} \ No newline at end of file diff --git a/routes/userRoutes.js b/routes/userRoutes.js index e787cc4a..647137ad 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -100,7 +100,7 @@ router.get('/:id/subscriptions', async (req, res) => { const user = await User.findById(userId).select('subscriptions'); if (!user) { - return res.status(404).json({ message: 'User not found' }); + return res.status(204); } const subscribedCourses = user.subscriptions; From c0ca1d835280da483fdc0af60e653f335363191b Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 14:12:51 +0200 Subject: [PATCH 098/129] fix(router): fixed problem with the router in authRouter Changes made: - removed any exports which was not router from authRoutes.js --- __tests__/routes/authRoutes.test.js | 20 -------------------- package.json | 11 ++++++----- routes/authRoutes.js | 2 +- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index 40d9a60a..f8abf473 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -113,26 +113,6 @@ describe('Login User route', () => { }); }); -describe('generatePasswordResetToken', () => { - it('Returns a string of four digits', async () => { - const token = generatePasswordResetToken(); - expect(typeof token).toBe('string'); - expect(token.length).toBe(4); - expect(Number.isInteger(Number(token))).toBe(true); - }); -}); - -describe('getRandomNumber', () => { - it('Returns a number between min and max', async () => { - const min = 0; - const max = 9; - const number = getRandomNumber(min, max); - expect(typeof number).toBe('number'); - expect(number).toBeGreaterThanOrEqual(min); - expect(number).toBeLessThanOrEqual(max); - }); -}); - describe('Reset password request route', () => { beforeAll(async () => { diff --git a/package.json b/package.json index 0ff3ac63..f6add8e5 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ "start": "node index.js", "server": "nodemon index.js", "test": "jest --runInBand", - "dev": "NODE_ENV=development npm run server", - "staging": "NODE_ENV=staging npm run server", - "prod": "NODE_ENV=production npm run server", + "dev": "cross-env NODE_ENV=development npm run server", + "staging": "cross-env NODE_ENV=staging npm run server", + "prod": "cross-env NODE_ENV=production npm run server", "docker": "docker-compose up -d", "docker_down": "docker-compose down", "docker_build": "docker-compose up -d --build", @@ -46,8 +46,8 @@ "nodemon": "^2.0.6", "passport": "^0.6.0", "passport-google-oauth20": "^2.0.0", - "supertest": "^6.3.3", - "prettier": "^3.0.3" + "prettier": "^3.0.3", + "supertest": "^6.3.3" }, "devDependencies": { "@babel/cli": "^7.2.3", @@ -55,6 +55,7 @@ "@babel/node": "^7.2.2", "@babel/preset-env": "^7.3.4", "babel-jest": "^24.4.0", + "cross-env": "^7.0.3", "jest": "^29.1.2", "mongodb-memory-server": "^8.9.3", "supertest": "^6.3.3" diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 3ae527fd..0f64e82c 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -179,4 +179,4 @@ function getRandomNumber(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } -module.exports = { generatePasswordResetToken, getRandomNumber, router }; // Export the functions for testing \ No newline at end of file +module.exports = router; // Export the functions for testing \ No newline at end of file From 3f603612aa083d21c4f6e470682fe097a76d94c0 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 14:29:48 +0200 Subject: [PATCH 099/129] fix(router): more fixes to the authRoutes Changes made: - Removed duplicated module.export - Fixed import of router in authRoutes.test.js --- __tests__/routes/authRoutes.test.js | 2 +- routes/authRoutes.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index f8abf473..8d82aba3 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -1,6 +1,6 @@ const request = require('supertest'); const express = require('express'); -const { router, generatePasswordResetToken, getRandomNumber } = require('../../routes/authRoutes'); // Import your router file here +const router = require('../../routes/authRoutes'); // Import your router file here const connectDb = require('../fixtures/db'); const makeFakeUser = require('../fixtures/fakeUser'); const mongoose = require('mongoose'); diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 0f64e82c..15dccf4b 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -152,8 +152,6 @@ router.get('/current_user', (req, res) => { }, 1500); }); -module.exports = router; - /** * Generates a random 4 digit code for password reset * @returns {String} - 4 digit code as a string From 02e18801715cf337841b960f92fdd2b7cd42fa1c Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 14:37:55 +0200 Subject: [PATCH 100/129] refactor(test): refactored some duplicated code --- __tests__/routes/signupRoutes.spec.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 2adaffa5..370117ea 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -22,6 +22,13 @@ describe('Signup User route', () => { let db; // Store the database connection + const userInput = { + firstName: 'test user', + lastName: 'test user', + email: 'test@email.com', + password: 'ABC123456!', + }; + beforeAll(async () => { db = await connectDb(); // Connect to the database @@ -29,17 +36,12 @@ describe('Signup User route', () => { await db.collection('users').insertOne(fakeUser); }); - it('Check that the endpoint saves the user in the database', async () => { - const input = { - firstName: 'test user', - lastName: 'test user', - email: 'test@email.com', - password: 'ABC123456!', - }; + it('Saves the user in the database', async () => { + const response = await request(`http://localhost:${PORT}`) .post('/api/signup/user') - .send(input) + .send(userInput) .expect(201); // Verify that the user was saved in the database @@ -50,15 +52,9 @@ describe('Signup User route', () => { it('Returns error if email is missing', async () => { - const input = { - firstName: 'test user', - lastName: 'test user', - password: 'ABC123456!', - }; - const response = await request(`http://localhost:${PORT}`) .post('/api/signup/user') - .send(input) + .send(userInput) .expect(400); expect(response.body.error.code).toBe('E0208'); From ae89f2c95174ab6927fa837c16a59c2a2f160650 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 15:04:58 +0200 Subject: [PATCH 101/129] fix console.log error --- index.js | 2 +- routes/courseRoutes.js | 2 -- routes/userRoutes.js | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 50ed8ea6..45f68aa3 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ const app = express(); // Configuration for listening, communicate to handlers // Simple logging middleware for testing app.use((req, res, next) => { let fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; - console.log(fullUrl); + //console.log(fullUrl); next(); }); app.use( diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index a69afb7d..a48d4c60 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -584,7 +584,6 @@ router.post('/:id/subscribe', async (req, res) => { let user = await User.findById(user_id); - console.log(user); res.send(user); } catch (error) { @@ -609,7 +608,6 @@ router.post('/:id/unsubscribe', async (req, res) => { let user = await User.findById(user_id); - console.log(id) res.send(user) } catch (error) { diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 647137ad..e787cc4a 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -100,7 +100,7 @@ router.get('/:id/subscriptions', async (req, res) => { const user = await User.findById(userId).select('subscriptions'); if (!user) { - return res.status(204); + return res.status(404).json({ message: 'User not found' }); } const subscribedCourses = user.subscriptions; From 5b1c18f0037e9c4c5dd7d5a6cea352aba48fc5d9 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 16:34:19 +0200 Subject: [PATCH 102/129] feat(api): Added safeguard to limit reset password attempts Changes made: - Added a field with the three latest attempts for resetting password as dates to user model - Added check to delete attempts after x minutes (5 for now) - Added check for the amount of attempts within x minutes, sending error if more than 2 - Added error code for too many requests by a user - Fixed some small errors in signupRoutes test - Added test for the password attempt check --- __tests__/routes/authRoutes.test.js | 20 ++ __tests__/routes/signupRoutes.spec.js | 7 +- helpers/errorCodes.js | 258 +++++++++++++------------- models/User.js | 1 + routes/authRoutes.js | 26 ++- 5 files changed, 179 insertions(+), 133 deletions(-) diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index 8d82aba3..0d3a8e33 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -7,6 +7,7 @@ const mongoose = require('mongoose'); const { encrypt } = require('../../helpers/password'); const { sendResetPasswordEmail } = require('../../helpers/email'); const exp = require('constants'); +const { User } = require('../../models/User'); const app = express(); @@ -158,6 +159,25 @@ describe('Reset password request route', () => { expect(res.body.error.code).toBe('E0004'); }); + it('Returns error if reset password attempts > 3', async () => { + const newFakeUser = makeFakeUser(); + // Set reset password attempts to have 3 attempts + newFakeUser.resetAttempts = [new Date(), new Date(), new Date()]; + newFakeUser.email = 'mail@test.com'; // Change email to avoid duplicate key error + await db.collection('users').insertOne(newFakeUser); + sendResetPasswordEmail.mockImplementation(() => true); + const user = { email: newFakeUser.email } + console.log(await db.collection('users').findOne({ email: newFakeUser.email })); + const res = await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-request') + .send(user) + .expect(400); + + expect(res.body.error.code).toBe('E0406'); + }); + + // TODO: Test if reset password attempts are deleted after 1 hour + afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection }); diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 7de9b857..7da6db3c 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -37,21 +37,20 @@ describe('Signup User route', () => { }); it('Saves the user in the database', async () => { - - const response = await request(`http://localhost:${PORT}`) .post('/api/signup/user') .send(userInput) .expect(201); // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ email: input.email }); + const user = await db.collection('users').findOne({ email: userInput.email }); expect(user).toBeDefined(); - expect(user.email).toBe(input.email); + expect(user.email).toBe(userInput.email); }); it('Returns error if email is missing', async () => { + userInput.email = ''; const response = await request(`http://localhost:${PORT}`) .post('/api/signup/user') .send(userInput) diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index a4c288a1..a5d5cdb3 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -1,138 +1,142 @@ module.exports = { - // E00 - General errors - E0000: { - code: 'E0000', - message: 'Unknown error' - }, - E0001: { - code: 'E0001', - message: 'Authentication token is invalid or expired.' - }, - E0002: { - code: 'E0002', - message: 'Users role does not have the necessary permissions for this action' - }, - E0003: { - code: 'E0003', - message: 'Server could not be reached' - }, + // E00 - General errors + E0000: { + code: 'E0000', + message: 'Unknown error' + }, + E0001: { + code: 'E0001', + message: 'Authentication token is invalid or expired.' + }, + E0002: { + code: 'E0002', + message: 'Users role does not have the necessary permissions for this action' + }, + E0003: { + code: 'E0003', + message: 'Server could not be reached' + }, E0004: { code: 'E0004', message: 'Email could not be sent' }, - // E01 - Login errors - E0101: { - code: 'E0101', - message: 'Invalid email' - }, - E0102: { - code: 'E0102', - message: 'Account is locked due to multiple failed login attempts. Please try again later.' - }, - E0103: { - code: 'E0103', - message: 'Account is not verified. Please check your email for a verification link.' - }, - E0104: { - code: 'E0104', - message: 'Account is suspended or blocked by an administrator.' - }, - E0105: { - code: 'E0105', - message: 'Invalid password' - }, - - // E02 - Signup errors - E0201: { - code: 'E0201', - message: 'User with the provided email already exists.' - }, - E0202: { - code: 'E0202', - message: 'Password does not meet the minimum requirements.' - }, - E0203: { - code: 'E0203', - message: 'Invalid email format.' - }, - E0204: { - code: 'E0204', - message: 'User registration is currently disabled.' - }, - E0205: { - code: 'E0205', - message: 'Could not send a verification email. Please try again later.' - }, - E0206: { - code: 'E0206', - message: 'Email must contain "@" and ".".' - }, - E0207: { - code: 'E0207', - message: 'Email must be at least 6 characters.' - }, - E0208: { - code: 'E0208', - message: 'Email is required.' - }, - E0209: { - code: 'E0209', - message: 'First and last name are required.' - }, - E0210: { - code: 'E0210', - message: 'Names must be between 1 and 50 characters.' - }, - E0211: { - code: 'E0211', - message: 'Name must only contain letters, spaces, hyphens and apostrophes.' - }, - E0212: { - code: 'E0212', - message: 'Password is required.' - }, + // E01 - Login errors + E0101: { + code: 'E0101', + message: 'Invalid email' + }, + E0102: { + code: 'E0102', + message: 'Account is locked due to multiple failed login attempts. Please try again later.' + }, + E0103: { + code: 'E0103', + message: 'Account is not verified. Please check your email for a verification link.' + }, + E0104: { + code: 'E0104', + message: 'Account is suspended or blocked by an administrator.' + }, + E0105: { + code: 'E0105', + message: 'Invalid password' + }, - // E03 - Logout errors - E0301: { - code: 'E0301', - message: 'User is not authenticated. Logout is not possible.' - }, + // E02 - Signup errors + E0201: { + code: 'E0201', + message: 'User with the provided email already exists.' + }, + E0202: { + code: 'E0202', + message: 'Password does not meet the minimum requirements.' + }, + E0203: { + code: 'E0203', + message: 'Invalid email format.' + }, + E0204: { + code: 'E0204', + message: 'User registration is currently disabled.' + }, + E0205: { + code: 'E0205', + message: 'Could not send a verification email. Please try again later.' + }, + E0206: { + code: 'E0206', + message: 'Email must contain "@" and ".".' + }, + E0207: { + code: 'E0207', + message: 'Email must be at least 6 characters.' + }, + E0208: { + code: 'E0208', + message: 'Email is required.' + }, + E0209: { + code: 'E0209', + message: 'First and last name are required.' + }, + E0210: { + code: 'E0210', + message: 'Names must be between 1 and 50 characters.' + }, + E0211: { + code: 'E0211', + message: 'Name must only contain letters, spaces, hyphens and apostrophes.' + }, + E0212: { + code: 'E0212', + message: 'Password is required.' + }, + + // E03 - Logout errors + E0301: { + code: 'E0301', + message: 'User is not authenticated. Logout is not possible.' + }, - // E04 - Password reset errors - E0401: { - code: 'E0401', - message: 'The provided email is not associated with any account.' - }, - E0402: { - code: 'E0402', - message: 'Password reset link has expired.' - }, - E0403: { - code: 'E0403', - message: 'Password reset link is invalid or has already been used.' - }, - E0404: { - code: 'E0404', - message: 'Password reset code has expired.' - }, - E0405: { - code: 'E0405', - message: 'Password reset code is invalid or has already been used.' - }, + // E04 - Password reset errors + E0401: { + code: 'E0401', + message: 'The provided email is not associated with any account.' + }, + E0402: { + code: 'E0402', + message: 'Password reset link has expired.' + }, + E0403: { + code: 'E0403', + message: 'Password reset link is invalid or has already been used.' + }, + E0404: { + code: 'E0404', + message: 'Password reset code has expired.' + }, + E0405: { + code: 'E0405', + message: 'Password reset code is invalid or has already been used.' + }, + E0406: { + code: 'E0406', + message: 'Too many requests. Please try again later.' + }, - // E05 - Verification errors - E0501: { - code: 'E0501', - message: 'Account is already verified.' - }, - E0502: { - code: 'E0502', - message: 'Verification link has expired.' - }, - E0503: { - code: 'E0503', - message: 'Verification link is invalid or has already been used.' - } +// E05 - Verification errors +E0501: { + code: 'E0501', + message: 'Account is already verified.' +}, +E0502: { + code: 'E0502', + message: 'Verification link has expired.' +}, +E0503: { + code: 'E0503', + message: 'Verification link is invalid or has already been used.' +} }; \ No newline at end of file diff --git a/models/User.js b/models/User.js index 836c74b8..7df344dd 100644 --- a/models/User.js +++ b/models/User.js @@ -73,6 +73,7 @@ const userSchema = new Schema({ }, password: String, joinedAt: Date, + resetAttempts: [Date], modifiedAt: Date }); diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 15dccf4b..9eea7d95 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -10,7 +10,8 @@ const send = require('send'); const { sendResetPasswordEmail } = require('../helpers/email'); const { PasswordResetToken } = require('../models/PasswordResetToken'); -const EXPIRATION_TIME = 1000 * 60 * 5; +const TOKEN_EXPIRATION_TIME = 1000 * 60 * 5; +const ATTEMPT_EXPIRATION_TIME = 1000 * 60 * 5; //1000 * 60 * 60; // Services //require("../services/passport"); @@ -83,6 +84,27 @@ router.post('/reset-password-request', async (req, res) => { return res.status(400).json({ error: errorCodes['E0401'] }); } + // Delete any attempts older than 1 hour + if(user.resetAttempts != null) { + user.resetAttempts.forEach(async (attempt) => { + if(attempt === null || attempt < (Date.now() - ATTEMPT_EXPIRATION_TIME) ) { + user.resetAttempts.remove(attempt); + await User.updateOne({ _id: user._id }, user); + } + }); + } else { + user.resetAttempts = []; + await User.updateOne({ _id: user._id }, user); + } + // If there are more than 2 attempts in the last hour, return error E0406 + if(user.resetAttempts.length > 2) { + return res.status(400).json({ error: errorCodes['E0406'] }); + } + + user.resetAttempts.push(Date.now()); + + await User.updateOne({ _id: user._id }, user); + // Delete any existing token let token = await PasswordResetToken.findOne({ userId: user._id }); if (token) await token.deleteOne(); @@ -95,7 +117,7 @@ router.post('/reset-password-request', async (req, res) => { await new PasswordResetToken({ userId: user._id, token: hash, - expiresAt: Date.now() + EXPIRATION_TIME // 5 minutes + expiresAt: Date.now() + TOKEN_EXPIRATION_TIME // 5 minutes }).save(); // Send email with reset token From edda904da2249f06e3c7573aa3a85dda2189c2a7 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 19:03:49 +0200 Subject: [PATCH 103/129] test(api): added testing of reset attempts resetting Changes made: - Refactored makeFakeUser to take in an email and password reset attempts - Added test to make sure attempts older than 5 minutes are deleted --- __tests__/fixtures/fakeUser.js | 7 ++++--- __tests__/routes/authRoutes.test.js | 29 +++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/__tests__/fixtures/fakeUser.js b/__tests__/fixtures/fakeUser.js index ec3ba81c..ac5b8bb1 100644 --- a/__tests__/fixtures/fakeUser.js +++ b/__tests__/fixtures/fakeUser.js @@ -1,14 +1,15 @@ const { encrypt } = require('../../helpers/password'); const mongoose = require('mongoose'); -module.exports = function makeFakeUser() { +module.exports = function makeFakeUser(email = 'fake@gmail.com', attempts = []) { return { - email: 'fake@gmail.com', + email: email, password: encrypt('ABC123456!'), googleID: '1234567891011', joinedAt: new Date(), modifiedAt: new Date(), firstName: 'Fake first name', - lastName: 'Fake last name' + lastName: 'Fake last name', + resetAttempts: attempts, } } diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index 0d3a8e33..6c2bd2dc 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -6,8 +6,6 @@ const makeFakeUser = require('../fixtures/fakeUser'); const mongoose = require('mongoose'); const { encrypt } = require('../../helpers/password'); const { sendResetPasswordEmail } = require('../../helpers/email'); -const exp = require('constants'); -const { User } = require('../../models/User'); const app = express(); @@ -160,14 +158,10 @@ describe('Reset password request route', () => { }); it('Returns error if reset password attempts > 3', async () => { - const newFakeUser = makeFakeUser(); - // Set reset password attempts to have 3 attempts - newFakeUser.resetAttempts = [new Date(), new Date(), new Date()]; - newFakeUser.email = 'mail@test.com'; // Change email to avoid duplicate key error + const newFakeUser = makeFakeUser('test@user.com', [new Date(), new Date(), new Date()]); // Create a new fake user with 3 reset password attempts await db.collection('users').insertOne(newFakeUser); sendResetPasswordEmail.mockImplementation(() => true); const user = { email: newFakeUser.email } - console.log(await db.collection('users').findOne({ email: newFakeUser.email })); const res = await request(`http://localhost:${PORT}`) .post('/api/auth/reset-password-request') .send(user) @@ -176,7 +170,26 @@ describe('Reset password request route', () => { expect(res.body.error.code).toBe('E0406'); }); - // TODO: Test if reset password attempts are deleted after 1 hour + it('clears reset password attempts after expiration', async () => { + const newFakeUser = makeFakeUser('user@test.com', [new Date() - 1000 * 60 * 5, new Date(), new Date()]); + // Set reset password attempts to have 3 attempts with one being 5 minutes ago + await db.collection('users').insertOne(newFakeUser); + sendResetPasswordEmail.mockImplementation(() => true); + + const user = { email: newFakeUser.email } + + await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-request') + .send(user) + .expect(200); + + const result = await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-request') + .send(user) + .expect(400); + + expect(result.body.error.code).toBe('E0406'); + }); afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection From dd8d226f872f4926cbe3bc633bf593b1bc1f9232 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 19:06:37 +0200 Subject: [PATCH 104/129] fix: Removed leftover console.logs Changes made: - Removed leftover console.logs - Removed globalConfigMongo.json to stop tracking it --- __tests__/middleware/requireLogin.spec.js | 4 +--- __tests__/routes/userRoutes.spec.js | 4 +--- __tests__/setup/globalConfigMongo.json | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 __tests__/setup/globalConfigMongo.json diff --git a/__tests__/middleware/requireLogin.spec.js b/__tests__/middleware/requireLogin.spec.js index d52e910e..b23eb508 100644 --- a/__tests__/middleware/requireLogin.spec.js +++ b/__tests__/middleware/requireLogin.spec.js @@ -11,9 +11,7 @@ app.use('/api/test', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5022; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Mocked token secret const TOKEN_SECRET = 'test'; diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index b56a294a..a95e0c61 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -12,9 +12,7 @@ app.use('/api/users', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5023; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Mocked token secret const TOKEN_SECRET = 'test'; diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json deleted file mode 100644 index 7af41009..00000000 --- a/__tests__/setup/globalConfigMongo.json +++ /dev/null @@ -1 +0,0 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:58035/"} \ No newline at end of file From 049b10f68a894fa6b2fb720b292f84003624a86e Mon Sep 17 00:00:00 2001 From: Jatewo Date: Thu, 12 Oct 2023 19:25:29 +0200 Subject: [PATCH 105/129] fix(tests): fixed some deprecation warning in tests Changes made: - Updated mongoose - Added some options to the mongoose database connection to use non-deprecated functions - Added two missing awaits in tests - Removed some leftover console.logs --- __tests__/fixtures/db.js | 4 +- __tests__/routes/signupRoutes.spec.js | 2 +- __tests__/routes/userRoutes.spec.js | 394 +++++++++++++------------- package.json | 2 +- routes/userRoutes.js | 2 - 5 files changed, 202 insertions(+), 202 deletions(-) diff --git a/__tests__/fixtures/db.js b/__tests__/fixtures/db.js index 1e54fa8f..deb9d1c5 100644 --- a/__tests__/fixtures/db.js +++ b/__tests__/fixtures/db.js @@ -9,7 +9,9 @@ module.exports = async function connectDb() { global.__MONGO_URI__, { useNewUrlParser: true, - useFindAndModify: false + useUnifiedTopology: true, + useFindAndModify: false, + useCreateIndex: true, } ) //db = db || connection.db(global.__MONGO_DB_NAME__) diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 7da6db3c..f55e9176 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -194,7 +194,7 @@ describe('Signup User route', () => { }); afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection server.close(); await mongoose.connection.close(); }); diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index a95e0c61..0b4d3155 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -19,205 +19,205 @@ const TOKEN_SECRET = 'test'; // Mock token secret jest.mock('../../config/keys', () => { - return { - TOKEN_SECRET - }; + return { + TOKEN_SECRET + }; }); describe('Update User Email Route', () => { - let token, fakeUser, db; - - beforeAll(async () => { - db = await connectDb(); // Connect to the database - - token = signAccessToken({ id: 1 }); - fakeUser = makeFakeUser(); - - }); - - beforeEach(async () => { - // Insert the fake user into the database before each test - await db.collection('users').insertOne(fakeUser); - }); - - afterEach(async () => { - // Remove the user from the database after each test - await db.collection('users').deleteOne({ _id: fakeUser._id }); - }); - - it('deletes a user successfully', async () => { - // Delete the user using the API - await request(`http://localhost:${PORT}`) - .delete(`/api/users/delete/${fakeUser._id}`) - .set('token', token) // Include the token in the request headers - .expect(200); - - // Verify that the user was deleted from the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeNull(); - }); - - it('handles user not found error for delete', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - - await request(`http://localhost:${PORT}`) - .delete(`/api/users/delete/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .expect(204); - }); - - it('updates user email successfully', async () => { - const newEmail = 'newemail@example.com'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ email: newEmail }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeDefined(); - expect(user.email).toBe(newEmail); - }); - - it('Test that emails must be unique when updating', async () => { - const newEmail = fakeUser.email; - - const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ email: newEmail }) - .expect(400); // Expecting a 400 Bad Request response - - expect(response.body.error.code).toBe('E0201'); - }); - - it('handles user not found error for update-email', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - const newEmail = 'newemail@example.com'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + nonExistentUserId) - .set('token', token) // Include the token in the request headers - .send({ email: newEmail }) - .expect(204); // Expecting a 204 No Content response for user not found - }); - - it('updates user first name successfully', async () => { - const newFirstName = 'newFirstName'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ firstName: newFirstName }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeDefined(); - expect(user.firstName).toBe(newFirstName); - }); - - it('handles user not found error for update-first-name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - - await request(`http://localhost:${PORT}`) - .patch(`/api/users/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .send({ newFirstName: 'NewFirstName' }) - .expect(204); - }); - - it('updates user last name successfully', async () => { - const newLastName = 'newLastName'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ lastName: newLastName }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(user).toBeDefined(); - expect(user.lastName).toBe(newLastName); - }); - - it('handles user not found error for update-last-name', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - - await request(`http://localhost:${PORT}`) - .patch(`/api/users/${nonExistentUserId}`) - .set('token', token) // Include the token in the request headers - .send({ newLastName: 'NewLastName' }) - .expect(204); - }); - - it('updates user fields successfully', async () => { - const newEmail = 'newemail@example.com'; - const newFirstName = 'Jane'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ - email: newEmail, - firstName: newFirstName - }) - .expect(200); // Expecting a 200 OK response - - // Verify that the user was updated in the database - const updatedUser = await db.collection('users').findOne({ _id: fakeUser._id }); - expect(updatedUser.email).toBe(newEmail); - expect(updatedUser.firstName).toBe(newFirstName); - }); - - it('handles user not found error', async () => { - const nonExistentUserId = new mongoose.Types.ObjectId(); - const newEmail = 'newemail@example.com'; - - await request(`http://localhost:${PORT}`) - .patch('/api/users/' + nonExistentUserId) - .set('token', token) // Include the token in the request headers - .send({ - email: newEmail - }) - .expect(204); // Expecting a 204 No Content response for user not found - }); - - it('handles validation errors for email', async () => { - const invalidEmail = 'invalidemail'; // Invalid email format - - const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ - email: invalidEmail - }) - .expect(400); // Expecting a 400 Bad Request response - - expect(response.body.error.code).toBe('E0206'); - }); - - it('handles validation errors for first name', async () => { - const invalidFirstName = 'AASD!==#ยค("DSN:_;>:'; // Invalid email format - - const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) - .set('token', token) // Include the token in the request headers - .send({ - firstName: invalidFirstName - }) - .expect(400); // Expecting a 400 Bad Request response - - expect(response.body.error.code).toBe('E0211'); - }); - - afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection - server.close(); - await mongoose.connection.close(); - }); + let token, fakeUser, db; + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + token = signAccessToken({ id: 1 }); + fakeUser = makeFakeUser(); + + }); + + beforeEach(async () => { + // Insert the fake user into the database before each test + await db.collection('users').insertOne(fakeUser); + }); + + afterEach(async () => { + // Remove the user from the database after each test + await db.collection('users').deleteOne({ _id: fakeUser._id }); + }); + + it('deletes a user successfully', async () => { + // Delete the user using the API + await request(`http://localhost:${PORT}`) + .delete(`/api/users/delete/${fakeUser._id}`) + .set('token', token) // Include the token in the request headers + .expect(200); + + // Verify that the user was deleted from the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeNull(); + }); + + it('handles user not found error for delete', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + + await request(`http://localhost:${PORT}`) + .delete(`/api/users/delete/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .expect(204); + }); + + it('updates user email successfully', async () => { + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ email: newEmail }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.email).toBe(newEmail); + }); + + it('Test that emails must be unique when updating', async () => { + const newEmail = fakeUser.email; + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ email: newEmail }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0201'); + }); + + it('handles user not found error for update-email', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers + .send({ email: newEmail }) + .expect(204); // Expecting a 204 No Content response for user not found + }); + + it('updates user first name successfully', async () => { + const newFirstName = 'newFirstName'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ firstName: newFirstName }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.firstName).toBe(newFirstName); + }); + + it('handles user not found error for update-first-name', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + + await request(`http://localhost:${PORT}`) + .patch(`/api/users/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .send({ newFirstName: 'NewFirstName' }) + .expect(204); + }); + + it('updates user last name successfully', async () => { + const newLastName = 'newLastName'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ lastName: newLastName }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was saved in the database + const user = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(user).toBeDefined(); + expect(user.lastName).toBe(newLastName); + }); + + it('handles user not found error for update-last-name', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + + await request(`http://localhost:${PORT}`) + .patch(`/api/users/${nonExistentUserId}`) + .set('token', token) // Include the token in the request headers + .send({ newLastName: 'NewLastName' }) + .expect(204); + }); + + it('updates user fields successfully', async () => { + const newEmail = 'newemail@example.com'; + const newFirstName = 'Jane'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + email: newEmail, + firstName: newFirstName + }) + .expect(200); // Expecting a 200 OK response + + // Verify that the user was updated in the database + const updatedUser = await db.collection('users').findOne({ _id: fakeUser._id }); + expect(updatedUser.email).toBe(newEmail); + expect(updatedUser.firstName).toBe(newFirstName); + }); + + it('handles user not found error', async () => { + const nonExistentUserId = new mongoose.Types.ObjectId(); + const newEmail = 'newemail@example.com'; + + await request(`http://localhost:${PORT}`) + .patch('/api/users/' + nonExistentUserId) + .set('token', token) // Include the token in the request headers + .send({ + email: newEmail + }) + .expect(204); // Expecting a 204 No Content response for user not found + }); + + it('handles validation errors for email', async () => { + const invalidEmail = 'invalidemail'; // Invalid email format + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + email: invalidEmail + }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0206'); + }); + + it('handles validation errors for first name', async () => { + const invalidFirstName = 'AASD!==#ยค("DSN:_;>:'; // Invalid email format + + const response = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + fakeUser._id) + .set('token', token) // Include the token in the request headers + .send({ + firstName: invalidFirstName + }) + .expect(400); // Expecting a 400 Bad Request response + + expect(response.body.error.code).toBe('E0211'); + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + server.close(); + await mongoose.connection.close(); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index f6add8e5..d31ed0be 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "http-proxy-middleware": "^2.0.3", "jsonwebtoken": "^8.5.1", "mongodb": "^6.1.0", - "mongoose": "^5.10.9", + "mongoose": "^5.13.20", "multer": "^1.4.2", "multer-s3": "^2.10.0", "nodemailer": "^6.8.0", diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 4554311f..4585f13d 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -25,7 +25,6 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { res.status(400); } - console.log(error); res.send({ error: error }); @@ -62,7 +61,6 @@ router.patch('/:id', requireLogin, async (req, res) => { res.status(400); } - console.log(error); res.send({ error: error }); From 268808d7c75a39a7d6a6e92cd9007c1f4eb5c47d Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Thu, 12 Oct 2023 19:55:33 +0200 Subject: [PATCH 106/129] damn, many tests and error handling fix --- __tests__/fixtures/fakeSection.js | 2 +- __tests__/routes/courseRoutes.spec.js | 362 +++++++++++++++++- __tests__/routes/userRoutes.spec.js | 100 ++++- __tests__/setup/globalConfigMongo.json | 2 +- ...s => contentCreatorApplicationListspec.js} | 0 helpers/errorCodes.js | 40 ++ models/Components.js | 10 +- routes/courseRoutes.js | 351 ++++++++++------- routes/userRoutes.js | 93 +++-- 9 files changed, 770 insertions(+), 190 deletions(-) rename applications/content-creator-applications/data-access/{notInUseTest_contentCreatorApplicationList.js => contentCreatorApplicationListspec.js} (100%) diff --git a/__tests__/fixtures/fakeSection.js b/__tests__/fixtures/fakeSection.js index fe74f0e3..906f8ea3 100644 --- a/__tests__/fixtures/fakeSection.js +++ b/__tests__/fixtures/fakeSection.js @@ -9,6 +9,6 @@ module.exports = function makeFakeSection() { modifiedAt: Date, totalPoints: 100, components: [], - parentCourse: '1abc', + parentCourse: '', }; }; \ No newline at end of file diff --git a/__tests__/routes/courseRoutes.spec.js b/__tests__/routes/courseRoutes.spec.js index c4cb5338..48a3335b 100644 --- a/__tests__/routes/courseRoutes.spec.js +++ b/__tests__/routes/courseRoutes.spec.js @@ -10,6 +10,7 @@ const CourseModel = require('../../models/Courses'); const SectionModel = require('../../models/Sections'); const ExerciseModel = require('../../models/Exercises'); const User = require('../../models/User'); +const { ObjectID } = require('mongodb'); const app = express(); app.use(express.json()); @@ -48,11 +49,14 @@ describe('Course Routes', () => { expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Array); - + // error handling for when there is no courses can be found in the bottom }); + }); describe('GET /courses/:id', () => { + + it('should get a specific course', async () => { const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id @@ -65,13 +69,54 @@ describe('Course Routes', () => { expect(response.body._id.toString()).toBe(courseId.toString()); }); + + it('should handle course not found error', async () => { + + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const courseId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0006'); + }); + + + it('should handle invalid course id', async () => { + + // simulate a request with invalid course id + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/this-is-an-invalid-courseId'); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + }); describe('GET /courses/:id/sections', () => { it('should get all sections from a course', async () => { + const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id; + const section = await db.collection('sections').findOne({ title: 'test section' }); + const sectionId = section._id; + + // add section to the course sections array + const resultCourse = await db.collection('courses').findOneAndUpdate( + { _id: courseId }, + { $push: { sections: sectionId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + const updatedCourse = resultCourse.value; + + // Check if the section was successfully added + expect(updatedCourse.sections.find((element) => element == sectionId)); + const response = await request(`http://localhost:${PORT}`) .get('/api/courses/' + courseId + '/sections'); @@ -79,9 +124,37 @@ describe('Course Routes', () => { expect(response.body).toBeInstanceOf(Array); }); + + it('should handle course not found error', async () => { + + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const courseId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections'); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0006'); + }); + + + it('should handle invalid course id', async () => { + + // simulate a request with invalid course id + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/this-is-an-invalid-courseId/sections'); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + + // error handling for no sections will be tested in the bottom }); describe('GET /courses/:courseId/sections/:sectionId', () => { + it('should get a specific section', async () => { const course = await db.collection('courses').findOne({ title: 'test course' }); @@ -90,13 +163,94 @@ describe('Course Routes', () => { const section = await db.collection('sections').findOne({ title: 'test section' }); const sectionId = section._id; + // Find the course and update its sections + const resultCourse = await db.collection('courses').findOneAndUpdate( + { _id: courseId }, + { $push: { sections: sectionId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + const updatedCourse = resultCourse.value; + + // update the section to have course as parent course: + const resultSection = await db.collection('sections').findOneAndUpdate( + { _id: sectionId }, + { $set: { parentCourse: courseId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + const updatedSection = resultSection.value; + + // Check if the section was successfully added + expect(updatedCourse.sections.find((element) => element == sectionId)); + // Check that parentCourse was successfully added + expect(updatedSection.parentCourse).toEqual(courseId); + const response = await request(`http://localhost:${PORT}`) .get('/api/courses/' + courseId + '/sections/' + sectionId); expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); + }); + + it('should handle course not found error', async () => { + + const section = await db.collection('sections').findOne({ title: 'test section' }); + const sectionId = section._id; + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const courseId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections/' + sectionId); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0006'); }); + + it('should handle invalid course id', async () => { + + const section = await db.collection('sections').findOne({ title: 'test section' }); + const sectionId = section._id; + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/this-is-an-invalid-courseId/sections/' + sectionId); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + + it('should handle section not found error', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const sectionId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections/' + sectionId); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0008'); + }); + + it('should handle invalid section id', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections/this-is-an-invalid-sectionId'); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + }); describe('POST /courses/:id/subscribe', () => { @@ -112,14 +266,76 @@ describe('Course Routes', () => { .post('/api/courses/' + courseId + '/subscribe') .send({ user_id: userId }); - console.log("course id: " + courseId) - console.log('response.body.subscriptions:', response.body.subscriptions) console.log(response.body.subscriptions.find((element) => element == courseId)) expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); expect(response.body.subscriptions.find((element) => element == courseId)); }); + it('should handle user not found error when subscribing', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // create non existing user id + const ObjectId = mongoose.Types.ObjectId; + const userId = new ObjectId('5f841c2b1c8cfb2c58b78d66'); + + // Simulate a request to unsubscribe with an non existing user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/subscribe') + .send({ user_id: userId }); + + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0004'); + }); + + it('should handle ivalid user id when subscribing', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // Simulate a request to unsubscribe with an invalid user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/subscribe') + .send({ user_id: 'this-is-an-invalid-userId' }); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + + it('should handle course not found error when subscribing', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const courseId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // Simulate a request to unsubscribe with an invalid user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/subscribe') + .send({ user_id: userId }); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0006'); + }); + + it('should handle invalid course id when subscribing', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // Simulate a request to unsubscribe with an invalid user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/this-is-aninvalid-courseId/subscribe') + .send({ user_id: userId }); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); }); describe('POST /courses/:id/unsubscribe', () => { @@ -140,8 +356,148 @@ describe('Course Routes', () => { expect(response.body.subscriptions.find((element) => element !== courseId)); }); + it('should handle user not found error when unsubscribing', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // create non existing user id + const ObjectId = mongoose.Types.ObjectId; + const userId = new ObjectId('5f841c2b1c8cfb2c58b78d66'); + + // Simulate a request to unsubscribe with an non existing user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/unsubscribe') + .send({ user_id: userId }); + + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0004'); + }); + + it('should handle ivalid user id when unsubscribing', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // Simulate a request to unsubscribe with an invalid user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/unsubscribe') + .send({ user_id: 'this-is-an-invalid-userId' }); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + + it('should handle course not found error when unsubscribing', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const courseId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // Simulate a request to unsubscribe with an invalid user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/' + courseId + '/unsubscribe') + .send({ user_id: userId }); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0006'); + }); + + it('should handle invalid course id when unsubscribing', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // Simulate a request to unsubscribe with an invalid user id + const response = await request(`http://localhost:${PORT}`) + .post('/api/courses/this-is-aninvalid-courseId/unsubscribe') + .send({ user_id: userId }); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); }); + describe('GET /courses/:id/sections, error handling', () => { + + it('should handle course does not contain sections', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + const resultCourse = await db.collection('courses').findOneAndUpdate( + { _id: courseId }, + { $set: { sections: [] } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + const updatedCourse = resultCourse.value; + + expect(updatedCourse.sections).toHaveLength(0); + + + // send request where there the course does not have sections + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections'); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0009'); + + + }); + + it('should handle sections not found', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + const section = await db.collection('sections').findOne({ title: 'test section' }); + const sectionId = section._id; + + // delete all sectionsn + await db.collection('sections').deleteMany({}); + + // add section to the course sections array + const resultCourse = await db.collection('courses').findOneAndUpdate( + { _id: courseId }, + { $push: { sections: sectionId } }, + { returnDocument: 'after' } // 'after' returns the updated document + ); + const updatedCourse = resultCourse.value; + + // Check if the section was successfully added + expect(updatedCourse.sections.find((element) => element == sectionId)); + + // send request where there are no sections in db that match the course's sections + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses/' + courseId + '/sections'); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0007'); + }); + }); + + describe('GET /courses, error handling', () => { + + it('should handle no courses not found', async () => { + + // delete all courses + await db.collection('courses').deleteMany({}); + + // send request with no courses in db + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses'); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0005'); + }); + + }); + + afterAll(async () => { db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index d708591d..c07897fd 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -230,7 +230,9 @@ describe('Users Routes', () => { describe('GET /users', () => { it('should check if a user is subscribed to a specific course and return true', async () => { - const courseId = '651d3a15cda7d5bd2878dfc7'; + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); const userId = user._id; @@ -244,7 +246,7 @@ describe('Users Routes', () => { const updatedUser = result.value; // Check if the subscription was successfully added - expect(updatedUser.subscriptions.includes(courseId)).toBe(true); + expect(updatedUser.subscriptions.find((element) => element == courseId)); const response = await request(`http://localhost:${PORT}`) .get('/api/users?user_id=' + userId + '&course_id=' + courseId); @@ -253,8 +255,6 @@ describe('Users Routes', () => { expect(response.text).toBe('true'); }); - - it('should return false if a user is not subscribed to a specific course', async () => { const course = await db.collection('courses').findOne({ title: 'test course' }); @@ -270,6 +270,70 @@ describe('Users Routes', () => { expect(response.text).toBe('false'); }); + + it('should handle user not found error', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // create non existing userId + const ObjectId = mongoose.Types.ObjectId; + const userId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent user + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0004'); + }); + + + it('should handle invalid user id', async () => { + + const course = await db.collection('courses').findOne({ title: 'test course' }); + const courseId = course._id; + + // simulate a request with invalid user id + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=this-is-an-invalid-userId&course_id=' + courseId); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + + it('should handle course not found error', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // create non existing courseId + const ObjectId = mongoose.Types.ObjectId; + const courseId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent course + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0006'); + }); + + + it('should handle invalid course id', async () => { + + const user = await db.collection('users').findOne({ email: 'fake@gmail.com' }); + const userId = user._id; + + // simulate a request with invalid course id + const response = await request(`http://localhost:${PORT}`) + .get('/api/users?user_id=' + userId + '&course_id=this-is-an-invalid-courseId'); + + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + }); describe('GET /users/:id/subscriptions', () => { @@ -289,7 +353,7 @@ describe('Users Routes', () => { const updatedUser = result.value; // Check if the subscription was successfully added - expect(updatedUser.subscriptions.includes(courseId)).toBe(true); + expect(updatedUser.subscriptions.find((element) => element == courseId)); const response = await request(`http://localhost:${PORT}`) .get('/api/users/' + userId + '/subscriptions'); @@ -299,6 +363,32 @@ describe('Users Routes', () => { expect(response.body.find((element) => element == courseId)); }); + + it('should handle user not found error', async () => { + + // create non existing userId + const ObjectId = mongoose.Types.ObjectId; + const userId = new ObjectId('5f841c2b1c8cfb2c58b78d68'); + + // simulate a request for a non-existent user + const response = await request(`http://localhost:${PORT}`) + .get('/api/users/' + userId + '/subscriptions'); + + expect(response.status).toBe(404); + expect(response.body.error.code).toBe('E0004'); + }); + + + it('should handle invalid user id', async () => { + + // simulate a request with invalid user id + const response = await request(`http://localhost:${PORT}`) + .get('/api/users/this-is-an-invalid-userId/subscriptions'); + + expect(response.status).toBe(500); + expect(response.body.error.code).toBe('E0003'); + }); + }); afterAll(async () => { diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index 1cbe8e46..541fc6e6 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:49426/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:53962/"} \ No newline at end of file diff --git a/applications/content-creator-applications/data-access/notInUseTest_contentCreatorApplicationList.js b/applications/content-creator-applications/data-access/contentCreatorApplicationListspec.js similarity index 100% rename from applications/content-creator-applications/data-access/notInUseTest_contentCreatorApplicationList.js rename to applications/content-creator-applications/data-access/contentCreatorApplicationListspec.js diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index 8ca6484c..11ef3cc5 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -21,6 +21,26 @@ module.exports = { code: 'E0004', message: 'User not found' }, + E0005: { + code: 'E0005', + message: 'No courses found' + }, + E0006: { + code: 'E0006', + message: 'Course not found' + }, + E0007: { + code: 'E0007', + message: 'No sections not found' + }, + E0008: { + code: 'E0008', + message: 'Section not found' + }, + E0009:{ + code: 'E0009', + message:'Course does not contain sections' + }, // E01 - Login errors E0101: { @@ -134,5 +154,25 @@ module.exports = { E0503: { code: 'E0503', message: 'Verification link is invalid or has already been used.' + }, + + // E06 - Subscription errors + + E0601: { + code: 'E0601', + message: 'Could not subscribe to course' + }, + E0602: { + code: 'E0602', + message: 'Could not unsubscribe to course' + }, + E0603: { + code: 'E0603', + message: 'Could not get users subscriptions' + }, + E0604: { + code: 'E0604', + message: 'Could not check users subscriptions' } + }; \ No newline at end of file diff --git a/models/Components.js b/models/Components.js index d48cea05..16142ed5 100644 --- a/models/Components.js +++ b/models/Components.js @@ -2,11 +2,11 @@ const mongoose = require("mongoose"); const { Schema } = mongoose; const component = new Schema({ - _id: Schema.Types.ObjectId, - fileName: String, - path: String, - size: Number, - type: String, + _id: Schema.Types.ObjectId, + fileName: String, + path: String, + size: Number, + type: String, }); module.exports = { component }; diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index a48d4c60..192d8991 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -1,9 +1,9 @@ const router = require('express').Router(); - +const errorCodes = require('../helpers/errorCodes'); const express = require('express'); const app = express(); app.use(express.json()); -app.use(express.urlencoded({extended: true})); +app.use(express.urlencoded({ extended: true })); // Models const { CourseModel } = require("../models/Courses"); @@ -11,7 +11,7 @@ const { SectionModel } = require("../models/Sections"); const { ComponentModel } = require("../models/Components"); const { User } = require("../models/User"); const { - ContentCreatorApplication, + ContentCreatorApplication, } = require("../models/ContentCreatorApplication"); const requireLogin = require("../middlewares/requireLogin"); const { UserModel } = require("../models/User"); @@ -92,31 +92,31 @@ router.post('/course/update', requireLogin, async (req, res) => { // Get all courses id router.get("/courses/all/id", async (req, res) => { - try { - // Searching for all courses in database - const course = await CourseModel.find(); - console.log("ID: " + course._id); - if (!course) { - // If no course is found, return an error message - console.log("No courses found") - return res.status(404).json({ - "message": "No courses found" - }); - } else { - console.log("ID: " + course.id); - return res.status(202).json({ - status: 'course fetched successful', - course: { - id: course.id, - }, - }); - } - } catch (err) { - console.log(err) - return res.status(500).json({ - "error": { "code": 500, "message": "Server could not be reached" } - }); - } + try { + // Searching for all courses in database + const course = await CourseModel.find(); + console.log("ID: " + course._id); + if (!course) { + // If no course is found, return an error message + console.log("No courses found") + return res.status(404).json({ + "message": "No courses found" + }); + } else { + console.log("ID: " + course.id); + return res.status(202).json({ + status: 'course fetched successful', + course: { + id: course.id, + }, + }); + } + } catch (err) { + console.log(err) + return res.status(500).json({ + "error": { "code": 500, "message": "Server could not be reached" } + }); + } }); @@ -246,13 +246,13 @@ router.post('/section/create', requireLogin, async (req, res) => { // Get all sections router.post("/course/sections", requireLogin, async (req, res) => { - const { sections } = req.body; - let list = []; - for (let i = 0; i < sections.length; i++) { - const temp = await SectionModel.findOne({ _id: sections[i] }); - list.push(temp); - } - res.send(list); + const { sections } = req.body; + let list = []; + for (let i = 0; i < sections.length; i++) { + const temp = await SectionModel.findOne({ _id: sections[i] }); + list.push(temp); + } + res.send(list); }); // Update section title @@ -349,7 +349,7 @@ router.post('/section/delete', requireLogin, async (req, res) => { // Create Component router.post("/component/create", async (req, res) => { - const { type, section_id } = req.body; // Or query?... + const { type, section_id } = req.body; // Or query?... const component = new ComponentModel({ type: type, @@ -372,13 +372,13 @@ router.post("/component/create", async (req, res) => { //Get all components router.post("/component/all", async (req, res) => { - const { components } = req.body; - let list = []; - for (let i = 0; i < components.length; i++) { - const temp = await ComponentModel.findOne({ _id: components[i] }); - list.push(temp); - } - res.send(list); + const { components } = req.body; + let list = []; + for (let i = 0; i < components.length; i++) { + const temp = await ComponentModel.findOne({ _id: components[i] }); + list.push(temp); + } + res.send(list); }); //Update Component order @@ -454,23 +454,23 @@ router.get('/course/delete_all', requireLogin, async (req, res) => { // User route router.post("/user/", async (req, res) => { - const { googleID } = req.body; - - const user = new UserModel({ - googleID: googleID, - email: email, - password: password, - joinedAt: Date.now(), - modifiedAt: Date.now(), - subscriptions: [] - }); - - try { - await user.save(); - res.send(user); - } catch (err) { - res.status(422).send(err); - } + const { googleID } = req.body; + + const user = new UserModel({ + googleID: googleID, + email: email, + password: password, + joinedAt: Date.now(), + modifiedAt: Date.now(), + subscriptions: [] + }); + + try { + await user.save(); + res.send(user); + } catch (err) { + res.status(422).send(err); + } }); */ @@ -481,33 +481,50 @@ router.post("/user/", async (req, res) => { //Get all courses router.get('', async (req, res) => { - try { - // find all courses in the database - const list = await CourseModel.find(); - res.send(list); + try { + // find all courses in the database + const courses = await CourseModel.find(); + + // check if sections exist + if (courses.length === 0) { + // Handle "courses not found" error response here + return res.status(404).json({ 'error': errorCodes['E0005'] }); + } + + res.send(courses); + + + } catch (error) { + // If the server could not be reached, return an error message + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } - }); // Get specific course router.get('/:id', async (req, res) => { - try { - const { id } = req.params; + try { + const { id } = req.params; + + // find a course based on it's id + const course = await CourseModel.findById(id); - // find a course based on it's id - const course = await CourseModel.findById(id); - res.send(course); + // check if courses exist + if (!course) { + // Handle "course not found" error response here + return res.status(404).json({ 'error': errorCodes['E0006'] }); + } + + res.send(course); + + } catch (error) { - } catch (error) { - console.error("Error getting course based on course id, message: " + error); - res.status(500).json({ message: 'Server error' }); - } + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } }) @@ -515,35 +532,69 @@ router.get('/:id', async (req, res) => { // Get all sections from course router.get('/:id/sections', async (req, res) => { - try { - const { id } = req.params; + try { + const { id } = req.params; + + // find a course based on it's id + const course = await CourseModel.findById(id); - // find all sections based on a course's id - const sections = await SectionModel.find({ parentCourse: id} ); + // check if courses exist + if (!course) { + // Handle "course not found" error response here + return res.status(404).json({ 'error': errorCodes['E0006'] }); + } + + const sectionsInCourse = course.sections; - res.send(sections); + // check if course contains sections + if (sectionsInCourse.length === 0) { + // Handle "course does not contain sections" error response here + return res.status(404).json({ 'error': errorCodes['E0009'] }); + } + + // check if the sections exists + const sectionsList = await SectionModel.find({ '_id': { $in: sectionsInCourse } }); + if (sectionsList.length === 0) { + // Handle "course does not contain sections" error response here + return res.status(404).json({ 'error': errorCodes['E0007'] }); + } - } catch (error) { - console.error("Error getting sections for course based on course id, message: " + error); - res.status(500).json({ message: 'Server error' }); - } + res.send(sectionsList); + + } catch (error) { + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } }); // Get a specififc section router.get('/:courseId/sections/:sectionId', async (req, res) => { - try { - const { courseId, sectionId } = req.params; + try { + const { courseId, sectionId } = req.params; - // find a specific section within the given course by both IDs - const section = await SectionModel.findOne({ parentCourse: courseId, _id: sectionId }); - res.send(section); + const course = await CourseModel.findById(courseId); + // check if courses exist + if (!course) { + // Handle "course not found" error response here + return res.status(404).json({ 'error': errorCodes['E0006'] }); + } + // find a specific section within the given course by both IDs + const section = await SectionModel.findOne({ parentCourse: courseId, _id: sectionId }); + + // check if section exist + if (!section) { + // Handle "section not found" error response here + return res.status(404).json({ 'error': errorCodes['E0008'] }); + } + + res.send(section); - } catch (error) { - console.error("Error getting specific section based on section id, message: " + error); - res.status(500).json({ message: 'Server error' }); - } + } catch (error) { + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } }); @@ -551,17 +602,17 @@ router.get('/:courseId/sections/:sectionId', async (req, res) => { // Get all excercies from a section *** commented out since we do not use it per 10/10 router.get("/:courseId/sections/:sectionId/exercises", async (req, res) => { - try { - const { courseId, sectionId } = req.params; + try { + const { courseId, sectionId } = req.params; - // find a specific section within the given course by both IDs - const exercises = await ExerciseModel.find({ parentSection: sectionId }); - res.send(exercises); + // find a specific section within the given course by both IDs + const exercises = await ExerciseModel.find({ parentSection: sectionId }); + res.send(exercises); - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Server error' }); + } }); */ @@ -569,53 +620,77 @@ router.get("/:courseId/sections/:sectionId/exercises", async (req, res) => { /*** SUBSCRIPTION ROUTES ***/ // Subscribe to course -router.post('/:id/subscribe', async (req, res) => { +router.post('/:id/subscribe', async (req, res) => { + + try { + const { id } = req.params; + const { user_id } = req.body; - try { - const { id } = req.params; - const { user_id} = req.body; + const user = await User.findById(user_id); - - // find user based on id, and add the course's id to the user's subscriptions field - (await User.findOneAndUpdate( - { _id: user_id }, - { $push: { subscriptions: id} })) - .save; + //checks if user exist + if (!user) { + // Handle "user not found" error response here + return res.status(404).json({ 'error': errorCodes['E0004'] }); + } + + const course = await CourseModel.findById(id); + // check if courses exist + if (!course) { + // Handle "course not found" error response here + return res.status(404).json({ 'error': errorCodes['E0006'] }); + } - let user = await User.findById(user_id); + // find user based on id, and add the course's id to the user's subscriptions field + (await User.findOneAndUpdate( + { _id: user_id }, + { $push: { subscriptions: id } })) + .save; - res.send(user); + res.send(user); - } catch (error) { - console.error("Error getting user to subscribe to course, message: " + error); - res.status(500).json({ message: 'Server error' }); - } + } catch (error) { + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } }); // Unsubscribe to course -router.post('/:id/unsubscribe', async (req, res) => { - - try { - const { id } = req.params; - const { user_id} = req.body; +router.post('/:id/unsubscribe', async (req, res) => { + + try { + const { id } = req.params; + const { user_id } = req.body; + + const user = await User.findById(user_id); + //checks if user exist + if (!user) { + // Handle "user not found" error response here + return res.status(404).json({ 'error': errorCodes['E0004'] }); + } - // find user based on id, and remove the course's id from the user's subscriptions field - (await User.findOneAndUpdate( - { _id: user_id }, - { $pull: { subscriptions: id} })) - .save; + const course = await CourseModel.findById(id); + // check if courses exist + if (!course) { + // Handle "course not found" error response here + return res.status(404).json({ 'error': errorCodes['E0006'] }); + } - let user = await User.findById(user_id); + // find user based on id, and remove the course's id from the user's subscriptions field + (await User.findOneAndUpdate( + { _id: user_id }, + { $pull: { subscriptions: id } })) + .save; - res.send(user) + res.send(user) - } catch (error) { - console.error(("Error getting user to unsubscribe, message: " + error)); - res.status(500).json({ message: 'Server error' }); - } + } catch (error) { + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } }); - + module.exports = router; diff --git a/routes/userRoutes.js b/routes/userRoutes.js index e787cc4a..61287141 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -18,19 +18,19 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { res.send(deletedUser) } - } catch (error) { - if (error === errorCodes['E0004']) { + } catch (error) { + if (error === errorCodes['E0004']) { // Handle "user not found" error response here res.status(204); } else { res.status(400); } - + console.log(error); res.send({ - error: error - }); - } + error: error + }); + } }); // Update User with dynamic fields @@ -62,11 +62,11 @@ router.patch('/:id', requireLogin, async (req, res) => { } else { res.status(400); } - + console.log(error); res.send({ - error: error - }); + error: error + }); } }); @@ -99,20 +99,23 @@ router.get('/:id/subscriptions', async (req, res) => { // Find the user by _id and select the 'subscriptions' field const user = await User.findById(userId).select('subscriptions'); + //checks if user exist if (!user) { - return res.status(404).json({ message: 'User not found' }); + // Handle "user not found" error response here + return res.status(404).json({ 'error': errorCodes['E0004'] }); } - + const subscribedCourses = user.subscriptions; // Find courses based on the subscribed course IDs - const list = await CourseModel.find({ '_id': { $in: subscribedCourses } }); + const courseList = await CourseModel.find({ '_id': { $in: subscribedCourses } }); - res.send(list); + res.send(courseList); } catch (error) { - console.error(("Error getting users subscribed courses, message: " + error)); - res.status(500).json({ message: 'Server error' }); + // If the server could not be reached, return an error message + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); } }); @@ -120,25 +123,41 @@ router.get('/:id/subscriptions', async (req, res) => { // Checks if user is subscribed to a specific course router.get('', async (req, res) => { - try { - const { user_id, course_id } = req.query; - - // Check if the course_id exists in the user's subscriptions array - const userTest = await User.findById(user_id); - - if (userTest.subscriptions.includes(course_id)) { - // User is subscribed to the course - res.send("true"); - } else { - // User is not subscribed to the course - res.send("false"); - } - } catch (error) { - console.error(("Error checking if user is subscribed to course, message: " + error)); - res.status(500).json({ message: 'Server error' }); - } - }); - - - - module.exports = router; \ No newline at end of file + try { + const { user_id, course_id } = req.query; + + // Check if the course_id exists in the user's subscriptions array + const user = await User.findById(user_id); + + //checks if user exist + if (!user) { + // Handle "user not found" error response here + return res.status(404).json({ 'error': errorCodes['E0004'] }); + } + + const course = await CourseModel.findById(course_id); + + // check if courses exist + if (!course) { + // Handle "course not found" error response here + return res.status(404).json({ 'error': errorCodes['E0006'] }); + } + + if (user.subscriptions.includes(course_id)) { + // User is subscribed to the course + res.send("true"); + } else { + // User is not subscribed to the course + res.send("false"); + } + + } catch (error) { + // If the server could not be reached, return an error message + console.log(error); + return res.status(500).json({ 'error': errorCodes['E0003'] }); + } +}); + + + +module.exports = router; \ No newline at end of file From 9a03ac4eb1a92ea835319fa15a54c4f18c09c3db Mon Sep 17 00:00:00 2001 From: Havkost <38227712+Havkost@users.noreply.github.com> Date: Thu, 12 Oct 2023 20:33:15 +0200 Subject: [PATCH 107/129] fix: Remove package-lock and keys.js from gitignore These files were added to the .gitignore, they should not be. --- .gitignore | 2 - package-lock.json | 18686 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 18686 insertions(+), 2 deletions(-) create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 3a3de912..cc403952 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,8 @@ node_modules config/dev.js config/educado-*.json config/gcp_service.json -config/keys.js *.DS_Store -package-lock.json dev.js config/.env *.idea diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..d2166869 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,18686 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "aws-sdk": "^2.1083.0", + "bcrypt": "^5.0.1", + "cli": "^1.0.1", + "concurrently": "^5.3.0", + "cookie-session": "^1.4.0", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "ejs": "^3.1.5", + "eslint": "^8.50.0", + "express": "^4.17.1", + "http-proxy-middleware": "^2.0.3", + "jsonwebtoken": "^8.5.1", + "mongodb": "^6.1.0", + "mongoose": "^5.10.9", + "multer": "^1.4.2", + "multer-s3": "^2.10.0", + "nodemailer": "^6.8.0", + "nodemon": "^2.0.6", + "passport": "^0.6.0", + "passport-google-oauth20": "^2.0.0", + "prettier": "^3.0.3", + "supertest": "^6.3.3" + }, + "devDependencies": { + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.4", + "@babel/node": "^7.2.2", + "@babel/preset-env": "^7.3.4", + "babel-jest": "^24.4.0", + "jest": "^29.1.2", + "mongodb-memory-server": "^8.9.3", + "supertest": "^6.3.3" + }, + "engines": { + "node": "12.18.3", + "npm": "6.14.6" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.414.0.tgz", + "integrity": "sha512-U9J0R/22eftQjY6JeAzXXYSgA+T6vx/jDsBBWBIKTjCZPBmmVOb2A4/d4sxHMxjh6Ms3DolpocE5FCbB9eGBEg==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.414.0", + "@aws-sdk/credential-provider-node": "3.414.0", + "@aws-sdk/middleware-host-header": "3.413.0", + "@aws-sdk/middleware-logger": "3.413.0", + "@aws-sdk/middleware-recursion-detection": "3.413.0", + "@aws-sdk/middleware-signing": "3.413.0", + "@aws-sdk/middleware-user-agent": "3.413.0", + "@aws-sdk/region-config-resolver": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@aws-sdk/util-endpoints": "3.413.0", + "@aws-sdk/util-user-agent-browser": "3.413.0", + "@aws-sdk/util-user-agent-node": "3.413.0", + "@smithy/config-resolver": "^2.0.8", + "@smithy/fetch-http-handler": "^2.1.3", + "@smithy/hash-node": "^2.0.7", + "@smithy/invalid-dependency": "^2.0.7", + "@smithy/middleware-content-length": "^2.0.9", + "@smithy/middleware-endpoint": "^2.0.7", + "@smithy/middleware-retry": "^2.0.10", + "@smithy/middleware-serde": "^2.0.7", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.10", + "@smithy/node-http-handler": "^2.1.3", + "@smithy/protocol-http": "^3.0.3", + "@smithy/smithy-client": "^2.1.4", + "@smithy/types": "^2.3.1", + "@smithy/url-parser": "^2.0.7", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.8", + "@smithy/util-defaults-mode-node": "^2.0.10", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.414.0.tgz", + "integrity": "sha512-GvRwQ7wA3edzsQEKS70ZPhkOUZ62PAiXasjp6GxrsADEb8sV1z4FxXNl9Un/7fQxKkh9QYaK1Wu1PmhLi9MLMg==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.413.0", + "@aws-sdk/middleware-logger": "3.413.0", + "@aws-sdk/middleware-recursion-detection": "3.413.0", + "@aws-sdk/middleware-user-agent": "3.413.0", + "@aws-sdk/region-config-resolver": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@aws-sdk/util-endpoints": "3.413.0", + "@aws-sdk/util-user-agent-browser": "3.413.0", + "@aws-sdk/util-user-agent-node": "3.413.0", + "@smithy/config-resolver": "^2.0.8", + "@smithy/fetch-http-handler": "^2.1.3", + "@smithy/hash-node": "^2.0.7", + "@smithy/invalid-dependency": "^2.0.7", + "@smithy/middleware-content-length": "^2.0.9", + "@smithy/middleware-endpoint": "^2.0.7", + "@smithy/middleware-retry": "^2.0.10", + "@smithy/middleware-serde": "^2.0.7", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.10", + "@smithy/node-http-handler": "^2.1.3", + "@smithy/protocol-http": "^3.0.3", + "@smithy/smithy-client": "^2.1.4", + "@smithy/types": "^2.3.1", + "@smithy/url-parser": "^2.0.7", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.8", + "@smithy/util-defaults-mode-node": "^2.0.10", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.414.0.tgz", + "integrity": "sha512-xeYH3si6Imp1EWolWn1zuxJJu2AXKwXl1HDftQULwC5AWkm1mNFbXYSJN4hQul1IM+kn+JTRB0XRHByQkKhe+Q==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.414.0", + "@aws-sdk/middleware-host-header": "3.413.0", + "@aws-sdk/middleware-logger": "3.413.0", + "@aws-sdk/middleware-recursion-detection": "3.413.0", + "@aws-sdk/middleware-sdk-sts": "3.413.0", + "@aws-sdk/middleware-signing": "3.413.0", + "@aws-sdk/middleware-user-agent": "3.413.0", + "@aws-sdk/region-config-resolver": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@aws-sdk/util-endpoints": "3.413.0", + "@aws-sdk/util-user-agent-browser": "3.413.0", + "@aws-sdk/util-user-agent-node": "3.413.0", + "@smithy/config-resolver": "^2.0.8", + "@smithy/fetch-http-handler": "^2.1.3", + "@smithy/hash-node": "^2.0.7", + "@smithy/invalid-dependency": "^2.0.7", + "@smithy/middleware-content-length": "^2.0.9", + "@smithy/middleware-endpoint": "^2.0.7", + "@smithy/middleware-retry": "^2.0.10", + "@smithy/middleware-serde": "^2.0.7", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.10", + "@smithy/node-http-handler": "^2.1.3", + "@smithy/protocol-http": "^3.0.3", + "@smithy/smithy-client": "^2.1.4", + "@smithy/types": "^2.3.1", + "@smithy/url-parser": "^2.0.7", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.8", + "@smithy/util-defaults-mode-node": "^2.0.10", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.414.0.tgz", + "integrity": "sha512-sAbp5HVy0YmvXUKkkTt8Tr5a5XNQMxshxI+pmXHLCHNpRATS9A6i7YHVNMGmERKGriFWZG0Q8qgKh1E+rvN7fQ==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.414.0", + "@aws-sdk/types": "3.413.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.413.0.tgz", + "integrity": "sha512-yeMOkfG20/RlzfPMtQuDB647AcPEvFEVYOWZzAWVJfldYQ5ybKr0d7sBkgG9sdAzGkK3Aw9dE4rigYI8EIqc1Q==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.414.0.tgz", + "integrity": "sha512-rlpLLx70roJL/t40opWC96LbIASejdMbRlgSCRpK8b/hKngYDe5A7SRVacaw08vYrAywxRiybxpQOwOt9b++rA==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.413.0", + "@aws-sdk/credential-provider-process": "3.413.0", + "@aws-sdk/credential-provider-sso": "3.414.0", + "@aws-sdk/credential-provider-web-identity": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.414.0.tgz", + "integrity": "sha512-xlkcOUKeGHInxWKKrZKIPSBCUL/ozyCldJBjmMKEj7ZmBAEiDcjpMe3pZ//LibMkCSy0b/7jtyQBE/eaIT2o0A==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.413.0", + "@aws-sdk/credential-provider-ini": "3.414.0", + "@aws-sdk/credential-provider-process": "3.413.0", + "@aws-sdk/credential-provider-sso": "3.414.0", + "@aws-sdk/credential-provider-web-identity": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.413.0.tgz", + "integrity": "sha512-GFJdgS14GzJ1wc2DEnS44Z/34iBZ05CAkvDsLN2CMwcDgH4eZuif9/x0lwzIJBK3xVFHzYUeVvEzsqRPbCHRsw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.414.0.tgz", + "integrity": "sha512-w9g2hlkZn7WekWICRqk+L33py7KrjYMFryVpkKXOx2pjDchCfZDr6pL1ml782GZ0L3qsob4SbNpbtp13JprnWQ==", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso": "3.414.0", + "@aws-sdk/token-providers": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.413.0.tgz", + "integrity": "sha512-5cdA1Iq9JeEHtg59ERV9fdMQ7cS0JF6gH/BWA7HYEUGdSVPXCuwyEggPtG64QgpNU7SmxH+QdDG+Ldxz09ycIA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.414.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.414.0.tgz", + "integrity": "sha512-xv3cN/lu+HRroGWYWtqwBQ+guykyjSKHzYq6FlyfBZyjC6u8hoeNSNilFobIooJOXfrDM0LKr9L+uUlPE+0+lg==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.414.0", + "@aws-sdk/client-sso": "3.414.0", + "@aws-sdk/client-sts": "3.414.0", + "@aws-sdk/credential-provider-cognito-identity": "3.414.0", + "@aws-sdk/credential-provider-env": "3.413.0", + "@aws-sdk/credential-provider-ini": "3.414.0", + "@aws-sdk/credential-provider-node": "3.414.0", + "@aws-sdk/credential-provider-process": "3.413.0", + "@aws-sdk/credential-provider-sso": "3.414.0", + "@aws-sdk/credential-provider-web-identity": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.413.0.tgz", + "integrity": "sha512-r9PQx468EzPHo9wRzZLfgROpKtVdbkteMrdhsuM12bifVHjU1OHr7yfhc1OdWv39X8Xiv6F8n5r+RBQEM0S6+g==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/protocol-http": "^3.0.3", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.413.0.tgz", + "integrity": "sha512-jqcXDubcKvoqBy+kkEa0WoNjG6SveDeyNy+gdGnTV+DEtYjkcHrHJei4q0W5zFl0mzc+dP+z8tJF44rv95ZY3Q==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.413.0.tgz", + "integrity": "sha512-C6k0IKJk/A4/VBGwUjxEPG+WOjjnmWAZVRBUzaeM7PqRh+g5rLcuIV356ntV3pREVxyiSTePTYVYIHU9YXkLKQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/protocol-http": "^3.0.3", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.413.0.tgz", + "integrity": "sha512-t0u//JUyaEZRVnH5q+Ur3tWnuyIsTdwA0XOdDCZXcSlLYzGp2MI/tScLjn9IydRrceIFpFfmbjk4Nf/Q6TeBTQ==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.413.0.tgz", + "integrity": "sha512-QFEnVvIKYPCermM+ESxEztgUgXzGSKpnPnohMYNvSZySqmOLu/4VvxiZbRO/BX9J3ZHcUgaw4vKm5VBZRrycxw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.3", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.3.1", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.413.0.tgz", + "integrity": "sha512-eVMJyeWxNBqerhfD+sE9sTjDtwQiECrfU6wpUQP5fGPhJD2cVVZPxuTuJGDZCu/4k/V61dF85IYlsPUNLdVQ6w==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@aws-sdk/util-endpoints": "3.413.0", + "@smithy/protocol-http": "^3.0.3", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.413.0.tgz", + "integrity": "sha512-h90e6yyOhvoc+1F5vFk3C5mxwB8RSDEMKTO/fxexyur94seczZ1yxyYkTMZv30oc9RUiToABlHNrh/wxL7TZPQ==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^2.0.10", + "@smithy/types": "^2.3.1", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.413.0.tgz", + "integrity": "sha512-NfP1Ib9LAWVLMTOa/1aJwt4TRrlRrNyukCpVZGfNaMnNNEoP5Rakdbcs8KFVHe/MJzU+GdKVzxQ4TgRkLOGTrA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.413.0", + "@aws-sdk/middleware-logger": "3.413.0", + "@aws-sdk/middleware-recursion-detection": "3.413.0", + "@aws-sdk/middleware-user-agent": "3.413.0", + "@aws-sdk/types": "3.413.0", + "@aws-sdk/util-endpoints": "3.413.0", + "@aws-sdk/util-user-agent-browser": "3.413.0", + "@aws-sdk/util-user-agent-node": "3.413.0", + "@smithy/config-resolver": "^2.0.8", + "@smithy/fetch-http-handler": "^2.1.3", + "@smithy/hash-node": "^2.0.7", + "@smithy/invalid-dependency": "^2.0.7", + "@smithy/middleware-content-length": "^2.0.9", + "@smithy/middleware-endpoint": "^2.0.7", + "@smithy/middleware-retry": "^2.0.10", + "@smithy/middleware-serde": "^2.0.7", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.10", + "@smithy/node-http-handler": "^2.1.3", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.3", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.1.4", + "@smithy/types": "^2.3.1", + "@smithy/url-parser": "^2.0.7", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.8", + "@smithy/util-defaults-mode-node": "^2.0.10", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/types": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.413.0.tgz", + "integrity": "sha512-j1xib0f/TazIFc5ySIKOlT1ujntRbaoG4LJFeEezz4ji03/wSJMI8Vi4KjzpBp8J1tTu0oRDnsxRIGixsUBeYQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.413.0.tgz", + "integrity": "sha512-VAwr7cITNb1L6/2XUPIbCOuhKGm0VtKCRblurrfUF2bxqG/wtuw/2Fm4ahYJPyxklOSXAMSq+RHdFWcir0YB/g==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.413.0.tgz", + "integrity": "sha512-7j/qWcRO2OBZBre2fC6V6M0PAS9n7k6i+VtofPkkhxC2DZszLJElqnooF9hGmVGYK3zR47Np4WjURXKIEZclWg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/types": "^2.3.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.413.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.413.0.tgz", + "integrity": "sha512-vHm9TVZIzfWMeDvdmoOky6VarqOt8Pr68CESHN0jyuO6XbhCDnr9rpaXiBhbSR+N1Qm7R/AfJgAhQyTMu2G1OA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.413.0", + "@smithy/node-config-provider": "^2.0.10", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-utf8-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@babel/cli": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.15.tgz", + "integrity": "sha512-prtg5f6zCERIaECeTZzd2fMtVjlfjhUcO+fBLQ6DXXdq5FljN+excVitJ2nogsusdf31LeqkjAfXZ7Xq+HmN8g==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz", + "integrity": "sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.20", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.20", + "@babel/types": "^7.22.19", + "convert-source-map": "^1.7.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" + } + }, + "node_modules/@babel/generator": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15", + "@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-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "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-member-expression-to-functions": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.15.tgz", + "integrity": "sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "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.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz", + "integrity": "sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==", + "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-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "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.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "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/node": { + "version": "7.22.19", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.22.19.tgz", + "integrity": "sha512-VsKSO9aEHdO16NdtqkJfrXZ9Sxlna1BVnBbToWr1KGdI3cyIk6KqOoa8mWvpK280lJDOwJqxvnl994KmLhq1Yw==", + "dev": true, + "dependencies": { + "@babel/register": "^7.22.15", + "commander": "^4.0.1", + "core-js": "^3.30.2", + "node-environment-flags": "^1.0.5", + "regenerator-runtime": "^0.14.0", + "v8flags": "^3.1.1" + }, + "bin": { + "babel-node": "bin/babel-node.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "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" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", + "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz", + "integrity": "sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz", + "integrity": "sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz", + "integrity": "sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz", + "integrity": "sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz", + "integrity": "sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz", + "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.20", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.15", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.15", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.15", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.15", + "@babel/plugin-transform-modules-systemjs": "^7.22.11", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-parameters": "^7.22.15", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.19", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/register": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", + "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", + "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.19", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.19", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", + "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.19", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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 + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/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==" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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" + } + }, + "node_modules/@eslint/eslintrc/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==" + }, + "node_modules/@eslint/js": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "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, + "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" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@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": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/console/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/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==", + "dev": true + }, + "node_modules/@jest/console/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.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.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/core/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/core/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, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/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==", + "dev": true + }, + "node_modules/@jest/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 + }, + "node_modules/@jest/core/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/core/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/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/@jest/core/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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" + } + }, + "node_modules/@jest/core/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/environment/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/environment/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/environment/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/environment/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/environment/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==", + "dev": true + }, + "node_modules/@jest/environment/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/fake-timers/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/fake-timers/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/fake-timers/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/fake-timers/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/fake-timers/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==", + "dev": true + }, + "node_modules/@jest/fake-timers/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/globals/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/globals/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/globals/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/globals/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/globals/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==", + "dev": true + }, + "node_modules/@jest/globals/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/globals/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/reporters/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/babel-plugin-istanbul/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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true + }, + "node_modules/@jest/reporters/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 + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument/node_modules/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==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/reporters/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/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/@jest/reporters/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/test-result/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/test-result/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/test-result/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/test-result/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/test-result/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==", + "dev": true + }, + "node_modules/@jest/test-result/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/test-result/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@jest/test-sequencer/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/test-sequencer/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/test-sequencer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/test-sequencer/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/test-sequencer/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==", + "dev": true + }, + "node_modules/@jest/test-sequencer/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/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/@jest/test-sequencer/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/test-sequencer/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "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.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/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": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/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==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/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" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/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==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", + "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.8.tgz", + "integrity": "sha512-2SOdVj5y0zE37Y9scSXoizoxgi6mgnDabi7a/SOfhl0p+50I0rIkuJTfyAuTPDtQ7e5dD6tSZPCLB3c/YM6Zig==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/abort-controller/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/config-resolver": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.9.tgz", + "integrity": "sha512-QBkGPLUqyPmis9Erz8v4q5lo/ErnF7+GD5WZHa6JZiXopUPfaaM+B21n8gzS5xCkIXZmnwzNQhObP9xQPu8oqQ==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^2.0.11", + "@smithy/types": "^2.3.2", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/config-resolver/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.11.tgz", + "integrity": "sha512-uJJs8dnM5iXkn8a2GaKvlKMhcOJ+oJPYqY9gY3CM/EieCVObIDjxUtR/g8lU/k/A+OauA78GzScAfulmFjPOYA==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^2.0.11", + "@smithy/property-provider": "^2.0.9", + "@smithy/types": "^2.3.2", + "@smithy/url-parser": "^2.0.8", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/eventstream-codec": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.8.tgz", + "integrity": "sha512-onO4to8ujCKn4m5XagReT9Nc6FlNG5vveuvjp1H7AtaG7njdet1LOl6/jmUOkskF2C/w+9jNw3r9Ak+ghOvN0A==", + "optional": true, + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.3.2", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/eventstream-codec/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.1.4.tgz", + "integrity": "sha512-SL24M9W5ERByoXaVicRx+bj9GJVujDnPn+QO7GY7adhY0mPGa6DSF58pVKsgIh4r5Tx/k3SWCPlH4BxxSxA/fQ==", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^3.0.4", + "@smithy/querystring-builder": "^2.0.8", + "@smithy/types": "^2.3.2", + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/fetch-http-handler/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/hash-node": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.8.tgz", + "integrity": "sha512-yZL/nmxZzjZV5/QX5JWSgXlt0HxuMTwFO89CS++jOMMPiCMZngf6VYmtNdccs8IIIAMmfQeTzwu07XgUE/Zd3Q==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/invalid-dependency": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.8.tgz", + "integrity": "sha512-88VOS7W3KzUz/bNRc+Sl/F/CDIasFspEE4G39YZRHIh9YmsXF7GUyVaAKURfMNulTie62ayk6BHC9O0nOBAVgQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/invalid-dependency/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/is-array-buffer/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/middleware-content-length": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.10.tgz", + "integrity": "sha512-EGSbysyA4jH0p3xI6G0jdXoj9Iz9GUnAta6aEaHtXm3wVWtenRf80y2TeVvNkVSr5jwKOdSCjKIRI2l1A/oZLA==", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^3.0.4", + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-content-length/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.8.tgz", + "integrity": "sha512-yOpogfG2d2V0cbJdAJ6GLAWkNOc9pVsL5hZUfXcxJu408N3CUCsXzIAFF6+70ZKSE+lCfG3GFErcSXv/UfUbjw==", + "optional": true, + "dependencies": { + "@smithy/middleware-serde": "^2.0.8", + "@smithy/types": "^2.3.2", + "@smithy/url-parser": "^2.0.8", + "@smithy/util-middleware": "^2.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/middleware-retry": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.11.tgz", + "integrity": "sha512-pknfokumZ+wvBERSuKAI2vVr+aK3ZgPiWRg6+0ZG4kKJogBRpPmDGWw+Jht0izS9ZaEbIobNzueIb4wD33JJVg==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^2.0.11", + "@smithy/protocol-http": "^3.0.4", + "@smithy/service-error-classification": "^2.0.1", + "@smithy/types": "^2.3.2", + "@smithy/util-middleware": "^2.0.1", + "@smithy/util-retry": "^2.0.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/middleware-retry/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==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.8.tgz", + "integrity": "sha512-Is0sm+LiNlgsc0QpstDzifugzL9ehno1wXp109GgBgpnKTK3j+KphiparBDI4hWTtH9/7OUsxuspNqai2yyhcg==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-serde/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/middleware-stack": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.1.tgz", + "integrity": "sha512-UexsfY6/oQZRjTQL56s9AKtMcR60tBNibSgNYX1I2WXaUaXg97W9JCkFyth85TzBWKDBTyhLfenrukS/kyu54A==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-stack/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/node-config-provider": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.11.tgz", + "integrity": "sha512-CaR1dciSSGKttjhcefpytYjsfI/Yd5mqL8am4wfmyFCDxSiPsvnEWHl8UjM/RbcAjX0klt+CeIKPSHEc0wGvJA==", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^2.0.9", + "@smithy/shared-ini-file-loader": "^2.0.10", + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-config-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/node-http-handler": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.4.tgz", + "integrity": "sha512-8Rw/AusvWDyC6SK8esAcVBeTlQHf94NMFv805suFUJCQ2gwlh0oLDNh+6s2MDOrxcjvLxjjzv1mytM0Mt+0cPQ==", + "optional": true, + "dependencies": { + "@smithy/abort-controller": "^2.0.8", + "@smithy/protocol-http": "^3.0.4", + "@smithy/querystring-builder": "^2.0.8", + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/property-provider": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.9.tgz", + "integrity": "sha512-25pPZ8f8DeRwYI5wbPRZaoMoR+3vrw8DwbA0TjP+GsdiB2KxScndr4HQehiJ5+WJ0giOTWhLz0bd+7Djv1qpUQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/property-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/protocol-http": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.4.tgz", + "integrity": "sha512-CGfSWk6TRlbwa8YgrSXdn80Yu7pov3EV/h7TSfiCHhq6/LO3WymmqnzgH1f0qV2bdTDipIkTNp5dGCGN3Af/5g==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/protocol-http/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/querystring-builder": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.8.tgz", + "integrity": "sha512-+vzIMwjC8Saz97/ptPn+IJRCRRZ+pP95ZIWDRqEqZV/a6hiKbaFoMSa2iCKsnKzR696U2JZXrDqMu3e/FD1+2g==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "@smithy/util-uri-escape": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-builder/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.8.tgz", + "integrity": "sha512-ArbanNuR7O/MmTd90ZqhDqGOPPDYmxx3huHxD+R3cuCnazcK/1tGQA+SnnR5307T7ZRb5WTpB6qBggERuibVSA==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.1.tgz", + "integrity": "sha512-QHa9+t+v4s0cMuDCcbjIJN67mNZ42/+fc3jKe8P6ZMPXZl5ksKk6a8vhZ/m494GZng5eFTc3OePv+NF9cG83yg==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.10.tgz", + "integrity": "sha512-jWASteSezRKohJ7GdA7pHDvmr7Q7tw3b5mu3xLHIkZy/ICftJ+O7aqNaF8wklhI7UNFoQ7flFRM3Rd0KA+1BbQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/signature-v4": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.8.tgz", + "integrity": "sha512-qrtiYMzaLlQ5HSJOaFwnyTQ3JLjmPY+3+pr9IBDpCVM6YtVj22cBLVB9bPOiZMIpkdI7ZRdxLBFlIjh5CO1Bhw==", + "optional": true, + "dependencies": { + "@smithy/eventstream-codec": "^2.0.8", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/types": "^2.3.2", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-middleware": "^2.0.1", + "@smithy/util-uri-escape": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/smithy-client": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.5.tgz", + "integrity": "sha512-7S865uKzsxApM8W8Q6zkij7tcUFgaG8PuADMFdMt1yL/ku3d0+s6Zwrg3N7iXCPM08Gu/mf0BIfTXIu/9i450Q==", + "optional": true, + "dependencies": { + "@smithy/middleware-stack": "^2.0.1", + "@smithy/types": "^2.3.2", + "@smithy/util-stream": "^2.0.11", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/types": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.3.2.tgz", + "integrity": "sha512-iH0cdKi7HQlzfAM3w2shFk/qZYKAqJWswtpmQpPtlruF+uFZeGEpMJjgDRyhWiddfVM4e2oP4nMaOBsMy6lXgg==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/url-parser": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.8.tgz", + "integrity": "sha512-wQw7j004ScCrBRJ+oNPXlLE9mtofxyadSZ9D8ov/rHkyurS7z1HTNuyaGRj6OvKsEk0SVQsuY0C9+EfM75XTkw==", + "optional": true, + "dependencies": { + "@smithy/querystring-parser": "^2.0.8", + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/url-parser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", + "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-body-length-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", + "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.9.tgz", + "integrity": "sha512-JONLJVQWT8165XoSV36ERn3SVlZLJJ4D6IeGsCSePv65Uxa93pzSLE0UMSR9Jwm4zix7rst9AS8W5QIypZWP8Q==", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^2.0.9", + "@smithy/smithy-client": "^2.1.5", + "@smithy/types": "^2.3.2", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.11.tgz", + "integrity": "sha512-tmqjNsfj+bgZN6jXBe6efZnukzILA7BUytHkzqikuRLNtR+0VVchQHvawD0w6vManh76rO81ydhioe7i4oBzuA==", + "optional": true, + "dependencies": { + "@smithy/config-resolver": "^2.0.9", + "@smithy/credential-provider-imds": "^2.0.11", + "@smithy/node-config-provider": "^2.0.11", + "@smithy/property-provider": "^2.0.9", + "@smithy/smithy-client": "^2.1.5", + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-middleware": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.1.tgz", + "integrity": "sha512-LnsBMi0Mg3gfz/TpNGLv2Jjcz2ra1OX5HR/4IaCepIYmtPQzqMWDdhX/XTW1LS8OZ0xbQuyQPcHkQ+2XkhWOVQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-middleware/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.1.tgz", + "integrity": "sha512-naj4X0IafJ9yJnVJ58QgSMkCNLjyQOnyrnKh/T0f+0UOUxJiT8vuFn/hS7B/pNqbo2STY7PyJ4J4f+5YqxwNtA==", + "optional": true, + "dependencies": { + "@smithy/service-error-classification": "^2.0.1", + "@smithy/types": "^2.3.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-retry/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-stream": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.11.tgz", + "integrity": "sha512-2MeWfqSpZKdmEJ+tH8CJQSgzLWhH5cmdE24X7JB0hiamXrOmswWGGuPvyj/9sQCTclo57pNxLR2p7KrP8Ahiyg==", + "optional": true, + "dependencies": { + "@smithy/fetch-http-handler": "^2.1.4", + "@smithy/node-http-handler": "^2.1.4", + "@smithy/types": "^2.3.2", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-stream/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-uri-escape/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", + "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", + "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", + "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/bson": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", + "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.12", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz", + "integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@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==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@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==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "dependencies": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/yargs": { + "version": "13.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", + "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "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==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "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==" + }, + "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==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/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/are-we-there-yet/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" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "optional": true, + "peer": true + }, + "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/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.6.tgz", + "integrity": "sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/async-mutex": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", + "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/async-mutex/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/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, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1460.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1460.0.tgz", + "integrity": "sha512-7K+aTMKtCAyR3cWxyGFd9YP1U4go4IaRsiFO8JLNpy2fSqlGC5XENGyLhmIQQOBwqb2WOwz5e1BftaKycmreWw==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "dependencies": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "dependencies": { + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.31.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "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, + "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-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/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==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bl/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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true + }, + "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==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "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.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/bson": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.1.0.tgz", + "integrity": "sha512-yiQ3KxvpVoRpx1oD1uPz4Jit9tAVTJgjdmjDKtUErkOoL9VNoF8Dd58qtAOL5E40exx2jvAT9sqdRSK/r+SHlA==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "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==" + }, + "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==" + }, + "node_modules/busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", + "dependencies": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "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" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001538", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001538.tgz", + "integrity": "sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==", + "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/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, + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "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==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "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, + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "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 + }, + "node_modules/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, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", + "dependencies": { + "exit": "0.1.2", + "glob": "^7.1.1" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/cliui/node_modules/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": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui/node_modules/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==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "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/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "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==" + }, + "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==", + "bin": { + "color-support": "bin.js" + } + }, + "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==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "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" + } + }, + "node_modules/concat-stream/node_modules/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==" + }, + "node_modules/concat-stream/node_modules/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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concurrently": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.3.0.tgz", + "integrity": "sha512-8MhqOB6PWlBfA2vJ8a0bSFKATOdWlHiQlk11IfmQBPaHVP8oP2gsh2MObE6UR3hqDHqvaIvLTyceNW6obVuFHQ==", + "dependencies": { + "chalk": "^2.4.2", + "date-fns": "^2.0.1", + "lodash": "^4.17.15", + "read-pkg": "^4.0.1", + "rxjs": "^6.5.2", + "spawn-command": "^0.0.2-1", + "supports-color": "^6.1.0", + "tree-kill": "^1.2.2", + "yargs": "^13.3.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "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 + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-session": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-1.4.0.tgz", + "integrity": "sha512-0hhwD+BUIwMXQraiZP/J7VP2YFzqo6g4WqZlWHtEHQ22t0MeZZrNBSCxC1zcaLAs8ApT3BzAKizx9gW/AP9vNA==", + "dependencies": { + "cookies": "0.8.0", + "debug": "2.6.9", + "on-headers": "~1.0.2" + } + }, + "node_modules/cookie-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/cookie-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, + "node_modules/cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", + "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.2.tgz", + "integrity": "sha512-+GjlguTDINOijtVRUxrQOv3kfu9rl+qPNdX2LTbJ/ZyVTuxK+ksVSAGX1nHstu4hrv1En/uPTtWgq2gI5wt4AQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/create-jest/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/create-jest/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-jest/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/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==", + "dev": true + }, + "node_modules/create-jest/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/create-jest/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "optional": true, + "peer": true + }, + "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==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "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 + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==", + "dependencies": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "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==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.523", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.523.tgz", + "integrity": "sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "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==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "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==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "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==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.51.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/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" + } + }, + "node_modules/eslint/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" + } + }, + "node_modules/eslint/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==" + }, + "node_modules/eslint/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" + } + }, + "node_modules/eslint/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" + } + }, + "node_modules/eslint/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==" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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" + } + }, + "node_modules/eslint/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" + } + }, + "node_modules/eslint/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==" + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "dev": true + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/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, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/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, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/expect/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/expect/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expect/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expect/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expect/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==", + "dev": true + }, + "node_modules/expect/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/expect/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + }, + "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==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "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==", + "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": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "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==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/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" + } + }, + "node_modules/gauge/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": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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" + } + }, + "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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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 + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==" + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" + }, + "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 + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "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" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "engines": { + "node": ">= 4" + } + }, + "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": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "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, + "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" + } + }, + "node_modules/import-local/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/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/import-local/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/import-local/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/import-local/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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "devOptional": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/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 + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "dependencies": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "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-report/node_modules/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==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "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, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/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" + } + }, + "node_modules/jake/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" + } + }, + "node_modules/jake/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" + } + }, + "node_modules/jake/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==" + }, + "node_modules/jake/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" + } + }, + "node_modules/jake/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" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-changed-files/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-changed-files/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-changed-files/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-changed-files/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-changed-files/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==", + "dev": true + }, + "node_modules/jest-changed-files/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/jest-changed-files/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" + } + }, + "node_modules/jest-changed-files/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/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" + } + }, + "node_modules/jest-changed-files/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-circus/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true + }, + "node_modules/jest-circus/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-cli/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/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, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/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==", + "dev": true + }, + "node_modules/jest-cli/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/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, + "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/jest-cli/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" + } + }, + "node_modules/jest-cli/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, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/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/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-config/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/jest-config/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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "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": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/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==", + "dev": true + }, + "node_modules/jest-config/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 + }, + "node_modules/jest-config/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-config/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/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/jest-config/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/jest-config/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true + }, + "node_modules/jest-diff/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-each/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true + }, + "node_modules/jest-each/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-environment-node/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-node/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-environment-node/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-environment-node/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-environment-node/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==", + "dev": true + }, + "node_modules/jest-environment-node/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "dependencies": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 6" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/jest-haste-map/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/jest-haste-map/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/jest-haste-map/node_modules/jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "dependencies": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-haste-map/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-haste-map/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-message-util/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true + }, + "node_modules/jest-message-util/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/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/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-mock/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-mock/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-mock/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-mock/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-mock/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==", + "dev": true + }, + "node_modules/jest-mock/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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 + } + } + }, + "node_modules/jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-resolve/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/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==", + "dev": true + }, + "node_modules/jest-resolve/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-resolve/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/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/jest-resolve/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-runner/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true + }, + "node_modules/jest-runner/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 + }, + "node_modules/jest-runner/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-runner/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/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/jest-runner/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/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, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-runtime/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true + }, + "node_modules/jest-runtime/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 + }, + "node_modules/jest-runtime/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-runtime/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/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/jest-runtime/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-snapshot/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true + }, + "node_modules/jest-snapshot/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 + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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/jest-snapshot/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-snapshot/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/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/jest-snapshot/node_modules/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==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "dependencies": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "dependencies": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "dependencies": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "node_modules/jest-util/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "dependencies": { + "@jest/types": "^24.9.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-util/node_modules/stack-utils": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-validate/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/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==", + "dev": true + }, + "node_modules/jest-validate/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-watcher/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watcher/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, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true + }, + "node_modules/jest-watcher/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/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" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest-worker/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-worker/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-worker/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-worker/node_modules/chalk/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-worker/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==", + "dev": true + }, + "node_modules/jest-worker/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.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": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest/node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/jest/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest/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==", + "dev": true + }, + "node_modules/jest/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, + "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 + }, + "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" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/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 + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "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, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "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/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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 + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/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, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/md5-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", + "integrity": "sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==", + "dev": true, + "bin": { + "md5-file": "cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==" + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "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 + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "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": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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": "*" + } + }, + "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" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/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, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mongodb": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.1.0.tgz", + "integrity": "sha512-AvzNY0zMkpothZ5mJAaIo2bGDjlJQqqAbn9fvtVgwIIUPEfdrqGxqNjjbuKyrgQxg2EvCmfWdjq+4uj96c0YPw==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.1.0", + "mongodb-connection-string-url": "^2.6.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-memory-server": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-8.15.1.tgz", + "integrity": "sha512-nqIbM5oh1s46VV4InhqQdNFu5szx+xi6qT//87beQ10JCZHLG1nZ/SUMsXkKLNn9wvs19OAf5HwI60enK9ZOuA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "mongodb-memory-server-core": "8.15.1", + "tslib": "^2.6.1" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/mongodb-memory-server-core": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-8.15.1.tgz", + "integrity": "sha512-U6ntro5DvUD71C2juKCzzc2Hul0qLYUpQLiXcXDcvtNDbGMoJgwKScdvptQkzQwUdICeZUfvZo8By7Mc09Umog==", + "dev": true, + "dependencies": { + "async-mutex": "^0.3.2", + "camelcase": "^6.3.0", + "debug": "^4.3.4", + "find-cache-dir": "^3.3.2", + "follow-redirects": "^1.15.2", + "get-port": "^5.1.1", + "https-proxy-agent": "^5.0.1", + "md5-file": "^5.0.0", + "mongodb": "^4.16.0", + "new-find-package-json": "^2.0.0", + "semver": "^7.5.4", + "tar-stream": "^2.1.4", + "tslib": "^2.6.1", + "uuid": "^9.0.0", + "yauzl": "^2.10.0" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dev": true, + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/mongodb-memory-server-core/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mongodb-memory-server-core/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mongodb-memory-server-core/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mongodb-memory-server-core/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==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mongodb-memory-server-core/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==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/mongodb": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.1.tgz", + "integrity": "sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==", + "dev": true, + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" + } + }, + "node_modules/mongodb-memory-server-core/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/mongodb-memory-server-core/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/mongodb-memory-server-core/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/mongodb-memory-server-core/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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/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==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/mongodb-memory-server-core/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/mongodb-memory-server-core/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mongodb-memory-server/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/mongoose": { + "version": "5.13.20", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.20.tgz", + "integrity": "sha512-TjGFa/XnJYt+wLmn8y9ssjyO2OhBMeEBtOHb9iJM16EWu2Du6L1Q6zSiEK2ziyYQM8agb4tumNIQFzqbxId7MA==", + "dependencies": { + "@types/bson": "1.x || 4.0.x", + "@types/mongodb": "^3.5.27", + "bson": "^1.1.4", + "kareem": "2.3.2", + "mongodb": "3.7.4", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.8.4", + "mquery": "3.2.5", + "ms": "2.1.2", + "optional-require": "1.0.x", + "regexp-clone": "1.0.0", + "safe-buffer": "5.2.1", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/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==", + "peerDependencies": { + "mongoose": "*" + } + }, + "node_modules/mongoose/node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/mongoose/node_modules/bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz", + "integrity": "sha512-K5q8aBqEXMwWdVNh94UQTwZ6BejVbFhh1uB6c5FKtPE9eUMZPUO3sRZdgIEcHSrAWmxzpG/FeODDKL388sqRmw==", + "dependencies": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/mongoose/node_modules/mongodb/node_modules/optional-require": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "dependencies": { + "require-at": "^1.0.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mongoose/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "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" + } + }, + "node_modules/mongoose/node_modules/readable-stream/node_modules/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==" + }, + "node_modules/mongoose/node_modules/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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/mongoose/node_modules/string_decoder/node_modules/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==" + }, + "node_modules/mpath": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", + "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", + "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "dependencies": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery/node_modules/bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "node_modules/mquery/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mquery/node_modules/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==" + }, + "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==" + }, + "node_modules/multer": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", + "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", + "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/multer-s3": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-2.10.0.tgz", + "integrity": "sha512-RZsiqG19C9gE82lB7v8duJ+TMIf70fWYHlIwuNcsanOH1ePBoPXZvboEQxEow9jUkk7WQsuyVA2TgriOuDrVrw==", + "dependencies": { + "file-type": "^3.3.0", + "html-comment-regex": "^1.1.2", + "run-parallel": "^1.1.6" + } + }, + "node_modules/nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "dev": true, + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "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": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/new-find-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/new-find-package-json/-/new-find-package-json-2.0.0.tgz", + "integrity": "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/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/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_modules/node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/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, + "bin": { + "semver": "bin/semver" + } + }, + "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==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/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==" + }, + "node_modules/node-fetch/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": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-notifier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", + "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.5", + "shellwords": "^0.1.1", + "uuid": "^8.3.2", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/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==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/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==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/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==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/nodemailer": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.5.tgz", + "integrity": "sha512-/dmdWo62XjumuLc5+AYQZeiRj+PRR8y8qKtFCOyuOl1k/hckZd8durUUHs/ucKx6/8kN+wFxqKJlQ/LK/qR5FA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "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.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "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==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "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==", + "bin": { + "semver": "bin/semver" + } + }, + "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" + } + }, + "node_modules/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==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/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/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.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, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, + "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/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz", + "integrity": "sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==", + "dev": true, + "dependencies": { + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "safe-array-concat": "^1.0.0" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "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/optional-require": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", + "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", + "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "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==", + "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==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "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 + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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/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, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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==" + }, + "node_modules/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, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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 + }, + "node_modules/read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha512-+UBirHHDm5J+3WDmLBZYSklRYg82nMlz+enn+GMZ22nSR2f4bzxmhso6rzQW/3mT2PVzpzDTiYIZahk8UmZ44w==", + "dependencies": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-pkg-up/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "dependencies": { + "util.promisify": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "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" + } + }, + "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, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "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" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "dev": true, + "dependencies": { + "@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" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "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/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + }, + "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==" + }, + "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==", + "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==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "devOptional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "devOptional": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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==" + }, + "node_modules/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==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.14.tgz", + "integrity": "sha512-U0eS5wcpu/O2/QZk6PcAMOA8H3ZuvRe4mFHA3Q+LNl1SRDmfQ+mD3RoD6tItqnvqubJ32m/zV2Z/ikSmxccD1Q==" + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "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, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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" + } + }, + "node_modules/string-width/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": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/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==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/superagent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/supertest": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", + "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", + "dev": true, + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.0.5" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "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==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "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/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/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==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar-stream/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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "dependencies": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "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 + }, + "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": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-regex-range/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==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "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==", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "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==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "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": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util.promisify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.2.tgz", + "integrity": "sha512-PBdZ03m1kBnQ5cjjO0ZvJMJS+QsbyIcFwi4hY4U76OQsCO9JrOYjbCFgIF76ccFg9xnJo7ZHPkqyj1GqmdS7MA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "object.getownpropertydescriptors": "^2.1.6", + "safe-array-concat": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "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, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/yargs/node_modules/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": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/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==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} From c691628144c6b5b7c2a9d116f5186922fa8148b2 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Fri, 13 Oct 2023 10:52:41 +0200 Subject: [PATCH 108/129] fix route and error message --- helpers/errorCodes.js | 2 +- routes/userRoutes.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index 11ef3cc5..95a75c96 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -31,7 +31,7 @@ module.exports = { }, E0007: { code: 'E0007', - message: 'No sections not found' + message: 'No sections found' }, E0008: { code: 'E0008', diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 61287141..42b03a75 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -122,7 +122,7 @@ router.get('/:id/subscriptions', async (req, res) => { // Checks if user is subscribed to a specific course -router.get('', async (req, res) => { +router.get('/subscriptions/', async (req, res) => { try { const { user_id, course_id } = req.query; From d44a98aa0a788a499cbc70e2893c1a85d428a5a6 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Fri, 13 Oct 2023 10:55:49 +0200 Subject: [PATCH 109/129] update tests in route --- __tests__/routes/userRoutes.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index c07897fd..5ab0ea7d 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -327,7 +327,7 @@ describe('Users Routes', () => { // simulate a request with invalid course id const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=this-is-an-invalid-courseId'); + .get('/api/users/subscriptions/?user_id=' + userId + '&course_id=this-is-an-invalid-courseId'); expect(response.status).toBe(500); From 236b12b6f1015a6fe81d2d55324fd097c0b4cfd2 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Fri, 13 Oct 2023 10:59:02 +0200 Subject: [PATCH 110/129] wopsi, route fix in all tests --- __tests__/routes/userRoutes.spec.js | 12 ++++++------ __tests__/setup/globalConfigMongo.json | 2 +- routes/userRoutes.js | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 5ab0ea7d..ddb66634 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -249,7 +249,7 @@ describe('Users Routes', () => { expect(updatedUser.subscriptions.find((element) => element == courseId)); const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + .get('/api/users/subscriptions?user_id=' + userId + '&course_id=' + courseId); expect(response.status).toBe(200); expect(response.text).toBe('true'); @@ -264,7 +264,7 @@ describe('Users Routes', () => { const userId = user._id; const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + .get('/api/users/subscriptions?user_id=' + userId + '&course_id=' + courseId); expect(response.status).toBe(200); expect(response.text).toBe('false'); @@ -282,7 +282,7 @@ describe('Users Routes', () => { // simulate a request for a non-existent user const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + .get('/api/users/subscriptions?user_id=' + userId + '&course_id=' + courseId); expect(response.status).toBe(404); expect(response.body.error.code).toBe('E0004'); @@ -296,7 +296,7 @@ describe('Users Routes', () => { // simulate a request with invalid user id const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=this-is-an-invalid-userId&course_id=' + courseId); + .get('/api/users/subscriptions?user_id=this-is-an-invalid-userId&course_id=' + courseId); expect(response.status).toBe(500); expect(response.body.error.code).toBe('E0003'); @@ -313,7 +313,7 @@ describe('Users Routes', () => { // simulate a request for a non-existent course const response = await request(`http://localhost:${PORT}`) - .get('/api/users?user_id=' + userId + '&course_id=' + courseId); + .get('/api/users/subscriptions?user_id=' + userId + '&course_id=' + courseId); expect(response.status).toBe(404); expect(response.body.error.code).toBe('E0006'); @@ -327,7 +327,7 @@ describe('Users Routes', () => { // simulate a request with invalid course id const response = await request(`http://localhost:${PORT}`) - .get('/api/users/subscriptions/?user_id=' + userId + '&course_id=this-is-an-invalid-courseId'); + .get('/api/users/subscriptions?user_id=' + userId + '&course_id=this-is-an-invalid-courseId'); expect(response.status).toBe(500); diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index 541fc6e6..bb119bc2 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:53962/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:55698/"} \ No newline at end of file diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 42b03a75..dff808b0 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -122,7 +122,7 @@ router.get('/:id/subscriptions', async (req, res) => { // Checks if user is subscribed to a specific course -router.get('/subscriptions/', async (req, res) => { +router.get('/subscriptions', async (req, res) => { try { const { user_id, course_id } = req.query; From 4ca24276d7313381ee54f230cec66d0c261bd80c Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Fri, 13 Oct 2023 12:16:54 +0200 Subject: [PATCH 111/129] fix --- __tests__/setup/globalConfigMongo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json index bb119bc2..293c7227 100644 --- a/__tests__/setup/globalConfigMongo.json +++ b/__tests__/setup/globalConfigMongo.json @@ -1 +1 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:55698/"} \ No newline at end of file +{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:55826/"} \ No newline at end of file From e08b1cf75fbf08dd3daed814cfded239e5b65cf4 Mon Sep 17 00:00:00 2001 From: sofiiagran Date: Fri, 13 Oct 2023 12:17:51 +0200 Subject: [PATCH 112/129] only removed the out-commented code --- routes/courseRoutes.js | 476 ----------------------------------------- 1 file changed, 476 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 192d8991..c87a8148 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -17,463 +17,6 @@ const requireLogin = require("../middlewares/requireLogin"); const { UserModel } = require("../models/User"); const { IdentityStore } = require("aws-sdk"); -//Why is all this out commented? Have it been replaced whit something else? -/* -// Content Creator Application Route -router.post('/course/', async (req, res) => { - const { title, description } = req.body; - - const course = new CourseModel({ - title: title, - description: description, - category: '', - _user: req.user.id, - dateCreated: Date.now(), - dateUpdated: Date.now(), - sections: [], - }); - - try { - await course.save(); - res.send(course); - } catch (err) { - res.status(422).send(err); - } -}); - -// Course routes - -router.post('/courses', async (req, res) => { - const { title, description } = req.body; - - const course = new CourseModel({ - title: title, - description: description, - category: '', - _user: req.user.id, - dateCreated: Date.now(), - dateUpdated: Date.now(), - sections: [], - }); - - try { - await course.save(); - res.send(course); - } catch (err) { - res.status(422).send(err); - } -}); - -// Update Course -router.post('/course/update', requireLogin, async (req, res) => { - const { course } = req.body; - const dbCourse = await CourseModel.findByIdAndUpdate( - course._id, - { - title: course.title, - description: course.description, - sections: course.sections, - }, - function (err, docs) { - if (err) { - console.log('Error:', err); - res.send(err); - } else { - console.log('Updated Course: ', docs); - } - } - ); - res.send('Course Update Complete'); -}); - - - - -// Get all courses id -router.get("/courses/all/id", async (req, res) => { - - try { - // Searching for all courses in database - const course = await CourseModel.find(); - console.log("ID: " + course._id); - if (!course) { - // If no course is found, return an error message - console.log("No courses found") - return res.status(404).json({ - "message": "No courses found" - }); - } else { - console.log("ID: " + course.id); - return res.status(202).json({ - status: 'course fetched successful', - course: { - id: course.id, - }, - }); - } - } catch (err) { - console.log(err) - return res.status(500).json({ - "error": { "code": 500, "message": "Server could not be reached" } - }); - } -}); - - -// Update course title -router.post('/course/update/title', async (req, res) => { - const { text, course_id } = req.body; - - // find object in database and update title to new value - (await CourseModel.findOneAndUpdate({ _id: course_id }, { title: text })) - .save; - course = await CourseModel.findById(course_id); - - // Send response - res.send(course); -}); - -// Update course description -router.post('/course/update/description', async (req, res) => { - const { text, course_id } = req.body; - - // find object in database and update title to new value - ( - await CourseModel.findOneAndUpdate( - { _id: course_id }, - { description: text } - ) - ).save; - course = await CourseModel.findById(course_id); - - // Send response - res.send(course); -}); - -// Update course category -router.post('/course/update/category', async (req, res) => { - const { text, course_id } = req.body; - - // find object in database and update title to new value - (await CourseModel.findOneAndUpdate({ _id: course_id }, { category: text })) - .save; - course = await CourseModel.findById(course_id); - - // Send response - res.send(course); -}); - -// Update course published state -router.post('/course/update/published', async (req, res) => { - const { published, course_id } = req.body; - - // find object in database and update title to new value - ( - await CourseModel.findOneAndUpdate( - { _id: course_id }, - { published: published } - ) - ).save; - course = await CourseModel.findById(course_id); - - // Send response - res.send(course); -}); - -// Delete all documents for user - the Nueclear option. -router.post('/course/delete', requireLogin, async (req, res) => { - const { course_id } = req.body; - let course; - try { - course = await CourseModel.findById(course_id).catch((err) => { - console.log(err); - }); - } catch (error) { - res.status(422).send(err); - } - const sectionIds = course.sections; - - sectionIds.map(async (section_id, index) => { - let section; - try { - section = await SectionModel.findById(section_id).catch((err) => { - console.log(err); - }); - } catch (error) { - res.status(422).send(err); - } - const componentIds = section.components; - componentIds.map(async (component_id, index) => { - await ComponentModel.deleteOne({ _id: component_id }, (err) => { - console.log(err); - }); - }); - await SectionModel.deleteOne({ _id: section_id }, (err) => { - console.log(err); - }); - }); - - await CourseModel.deleteOne({ _id: course_id }, (err) => { - console.log(err); - }); - - res.send('Completed'); -}); - -// Section routes - -router.post('/section/create', requireLogin, async (req, res) => { - const { title, course_id } = req.body; // Or query?... - - const section = new SectionModel({ - title: title, - description: '', - dateCreated: Date.now(), - dateUpdated: Date.now(), - components: [], - }); - - try { - await section.save(); - course = await CourseModel.findById(course_id); - await course.sections.push(section._id); - await course.save(); - res.send(course); - } catch (err) { - res.status(422).send(err); - } -}); - -// Get all sections -router.post("/course/sections", requireLogin, async (req, res) => { - const { sections } = req.body; - let list = []; - for (let i = 0; i < sections.length; i++) { - const temp = await SectionModel.findOne({ _id: sections[i] }); - list.push(temp); - } - res.send(list); -}); - -// Update section title -router.post('/course/update/sectiontitle', async (req, res) => { - // ... - // get new value & section ID - const { value, sectionId } = req.body; - - // find object in database and update title to new value - (await SectionModel.findOneAndUpdate({ _id: sectionId }, { title: value })) - .save; - - // Send response - res.send('Completed'); -}); - -// Update course description -router.post('/section/update/title', async (req, res) => { - const { text, section_id } = req.body; - - // find object in database and update title to new value - (await SectionModel.findOneAndUpdate({ _id: section_id }, { title: text })) - .save; - section = await SectionModel.findById(section_id); - - // Send response - res.send(section); -}); - -// Update section description -router.post('/section/update/description', async (req, res) => { - const { text, section_id } = req.body; - - // find object in database and update title to new value - ( - await SectionModel.findOneAndUpdate( - { _id: section_id }, - { description: text } - ) - ).save; - section = await SectionModel.findById(section_id); - - // Send response - res.send(section); -}); - -// Update sections order -router.post('/course/update/sectionsorder', async (req, res) => { - // Get sections from request - const { sections, course_id } = req.body; - // REPORT NOTE: Mรฅske lav performance test, for om det giver bedst mening at wipe array og overskrive, eller tjekke 1 efter 1 om updates - // Overwrite existing array - ( - await CourseModel.findOneAndUpdate( - { _id: course_id }, - { sections: sections } - ) - ).save; - - course = await CourseModel.findById(course_id); - - // Send response - res.send(course); -}); - -// Delete component for user -router.post('/section/delete', requireLogin, async (req, res) => { - const { section_id, course_id } = req.body; - - const course = await CourseModel.findById(course_id).catch((err) => { - console.log(err); - }); - - let sectionIds = course.sections; - - let index = sectionIds.indexOf(section_id); - if (index !== -1) { - sectionIds.splice(index, 1); - } - - ( - await CourseModel.findOneAndUpdate( - { _id: course_id }, - { sections: sectionIds } - ) - ).save; - - await SectionModel.deleteOne({ _id: section_id }, (err) => { - console.log(err); - }); - - res.send(sectionIds); -}); - -// Create Component -router.post("/component/create", async (req, res) => { - const { type, section_id } = req.body; // Or query?... - - const component = new ComponentModel({ - type: type, - file: '', - text: '', - dateCreated: Date.now(), - dateUpdated: Date.now(), - }); - - try { - await component.save(); - section = await SectionModel.findById(section_id); - await section.components.push(component._id); - await section.save(); - res.send(section); - } catch (err) { - res.status(422).send(err); - } -}); - -//Get all components -router.post("/component/all", async (req, res) => { - const { components } = req.body; - let list = []; - for (let i = 0; i < components.length; i++) { - const temp = await ComponentModel.findOne({ _id: components[i] }); - list.push(temp); - } - res.send(list); -}); - -//Update Component order -router.post('/component/updatecomponentorder', async (req, res) => { - // Get components from request - const { components, section_id } = req.body; - ( - await SectionModel.findOneAndUpdate( - { _id: section_id }, - { components: components } - ) - ).save; - section = await SectionModel.findById(section_id); - // Send response - res.send(section); -}); - -// Update section title -router.post('/component/text/update', async (req, res) => { - const { text, component_id } = req.body; - - // find object in database and update title to new value - (await ComponentModel.findOneAndUpdate({ _id: component_id }, { text: text })) - .save; - component = await ComponentModel.findById(component_id); - - // Send response - res.send(component); -}); - -// Delete all documents for user -router.post('/component/delete', requireLogin, async (req, res) => { - const { component_id, section_id } = req.body; - - const section = await SectionModel.findById(section_id).catch((err) => { - console.log(err); - }); - - let componentIds = section.components; - - let index = componentIds.indexOf(component_id); - if (index !== -1) { - componentIds.splice(index, 1); - } - - ( - await SectionModel.findOneAndUpdate( - { _id: section_id }, - { components: componentIds } - ) - ).save; - - await ComponentModel.deleteOne({ _id: component_id }, (err) => { - console.log(err); - }); - - res.send(componentIds); -}); - -// Delete all documents for user -router.get('/course/delete_all', requireLogin, async (req, res) => { - await CourseModel.deleteMany({ _user: req.user.id }, (err) => { - console.log(err); - }); - await SectionModel.deleteMany({}, (err) => { - console.log(err); - }); - await ComponentModel.deleteMany({}, (err) => { - console.log(err); - }); - res.send('Completed'); -}); - -// User route -router.post("/user/", async (req, res) => { - const { googleID } = req.body; - - const user = new UserModel({ - googleID: googleID, - email: email, - password: password, - joinedAt: Date.now(), - modifiedAt: Date.now(), - subscriptions: [] - }); - - try { - await user.save(); - res.send(user); - } catch (err) { - res.status(422).send(err); - } -}); - -*/ /*** COURSE, SECTIONS AND EXERCISE ROUTES ***/ @@ -598,25 +141,6 @@ router.get('/:courseId/sections/:sectionId', async (req, res) => { }); -/* -// Get all excercies from a section *** commented out since we do not use it per 10/10 -router.get("/:courseId/sections/:sectionId/exercises", async (req, res) => { - - try { - const { courseId, sectionId } = req.params; - - // find a specific section within the given course by both IDs - const exercises = await ExerciseModel.find({ parentSection: sectionId }); - res.send(exercises); - - } catch (error) { - console.error(error); - res.status(500).json({ message: 'Server error' }); - } - -}); -*/ - /*** SUBSCRIPTION ROUTES ***/ // Subscribe to course From f89a359c2034a069a5ac3f190ed117203cb43e63 Mon Sep 17 00:00:00 2001 From: Jacob Terpe Woods Date: Fri, 13 Oct 2023 12:54:58 +0200 Subject: [PATCH 113/129] fix: Delete __tests__/setup/globalConfigMongo.json - deleted json setup file to stop tracking of the file --- __tests__/setup/globalConfigMongo.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 __tests__/setup/globalConfigMongo.json diff --git a/__tests__/setup/globalConfigMongo.json b/__tests__/setup/globalConfigMongo.json deleted file mode 100644 index 293c7227..00000000 --- a/__tests__/setup/globalConfigMongo.json +++ /dev/null @@ -1 +0,0 @@ -{"mongoDBName":"jest","mongoUri":"mongodb://127.0.0.1:55826/"} \ No newline at end of file From e60c90679184b5e0fe4dc351043b7ec0f6ecee77 Mon Sep 17 00:00:00 2001 From: JackSMunn Date: Fri, 13 Oct 2023 13:10:34 +0200 Subject: [PATCH 114/129] feat: added endpoint for reset password code - Added fixture to create new resetPasswordToken - Added test for testing when a wrong token is submitted - Added endpoint to receive email and token from frontend in resetpassword pipeline - --- __tests__/fixtures/fakeResetPasswordToken.js | 8 ++++++ __tests__/routes/authRoutes.test.js | 21 +++++++++++++--- routes/authRoutes.js | 26 +++++++++++++++++--- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 __tests__/fixtures/fakeResetPasswordToken.js diff --git a/__tests__/fixtures/fakeResetPasswordToken.js b/__tests__/fixtures/fakeResetPasswordToken.js new file mode 100644 index 00000000..88d4f136 --- /dev/null +++ b/__tests__/fixtures/fakeResetPasswordToken.js @@ -0,0 +1,8 @@ +const mongoose = require('mongoose'); + +module.exports = function makeFakeResetPasswordToken(token='1234') { + return { + token: token, + expiresAt: new Date() + 1000 * 60 * 60 * 24 * 7, + } +} \ No newline at end of file diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index 6c2bd2dc..b5140aea 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -3,10 +3,12 @@ const express = require('express'); const router = require('../../routes/authRoutes'); // Import your router file here const connectDb = require('../fixtures/db'); const makeFakeUser = require('../fixtures/fakeUser'); +const makeFakeResetPasswordToken = require('../fixtures/fakeResetPasswordToken'); + const mongoose = require('mongoose'); const { encrypt } = require('../../helpers/password'); const { sendResetPasswordEmail } = require('../../helpers/email'); - +const token = require('../../helpers/token'); const app = express(); app.use(express.json()); @@ -154,6 +156,7 @@ describe('Reset password request route', () => { .post('/api/auth/reset-password-request') .send(existingEmail) .expect(500); + expect(res.body.error.code).toBe('E0004'); }); @@ -191,14 +194,24 @@ describe('Reset password request route', () => { expect(result.body.error.code).toBe('E0406'); }); + it('Returns error if token is wrong', async () => { + const fakeToken = makeFakeResetPasswordToken('6859'); + await db.collection('passwordResetTokenSchema').insertOne(fakeToken); + const fakeCredentials = {token: fakeToken.token, email: fakeUser.email}; + sendResetPasswordEmail.mockImplementation(() => false); + const res = await request(`http://localhost:${PORT}`) + .post('/api/auth/reset-password-code') + .send(fakeCredentials) + .expect(400); + + expect(res.body.error.code).toBe('E0405'); + }); + afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection }); }); -describe('Reset password route', () => { -}); - afterAll(async () => { await mongoose.connection.close(); server.close(); diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 9eea7d95..9af3db73 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -131,11 +131,32 @@ router.post('/reset-password-request', async (req, res) => { } }); +router.post('/reset-password-code', async (req, res) => { + const { email, token } = req.body; + const user = await User.findOne({ email: email }); + + // If email is not provided or user is not found, return error E0401 + if (!user) { + return res.status(400).json({ error: errorCodes['E0401'] }); + } + + const passwordResetToken = await PasswordResetToken.findOne({ userId: user._id}); + const isValid = compare(token, passwordResetToken.token); + + // If token is invalid, return error E0405 + if (!isValid) { + return res.status(400).json({ error: errorCodes['E0405'] }); + } + // return success + return res.status(200).json({ status: 'success' }); + +}); + router.put('/reset-password', async (req, res) => { const { email, token, newPassword } = req.body; const user = await User.findOne({ email: email }); - if (!email || !user) { // If email is not provided or user is not found, return error E0401 + if (!user) { // If email is not provided or user is not found, return error E0401 return res.status(400).json({ error: errorCodes['E0401'] }); } const passwordResetToken = await PasswordResetToken.findOne({ userId: user._id}); @@ -144,7 +165,7 @@ router.put('/reset-password', async (req, res) => { if (!passwordResetToken || passwordResetToken.expiresAt < Date.now()) { return res.status(400).json({ error: errorCodes['E0404'] }); } - const isValid = await compare(token, passwordResetToken.token); + const isValid = compare(token, passwordResetToken.token); // If token is invalid, return error E0405 if (!isValid) { @@ -158,7 +179,6 @@ router.put('/reset-password', async (req, res) => { // Return success return res.status(200).json({ status: 'success' }); - }); // Logout simulation From 51eb7af8aa63252390e960de99ced9833e068dab Mon Sep 17 00:00:00 2001 From: Jatewo Date: Fri, 13 Oct 2023 14:24:51 +0200 Subject: [PATCH 115/129] fix: added missing awaits and missing comma in model Changes made: - Added awaits in two test files - Added missing comma in User model --- __tests__/routes/courseRoutes.spec.js | 8 ++++---- __tests__/routes/userRoutes.spec.js | 8 ++++---- models/User.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/__tests__/routes/courseRoutes.spec.js b/__tests__/routes/courseRoutes.spec.js index 48a3335b..2d0a3226 100644 --- a/__tests__/routes/courseRoutes.spec.js +++ b/__tests__/routes/courseRoutes.spec.js @@ -500,10 +500,10 @@ describe('Course Routes', () => { afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection - db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection - db.collection('sections').deleteMany({}); // Delete all documents in the 'sections' collection - server.close(); + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + await db.collection('sections').deleteMany({}); // Delete all documents in the 'sections' collection + await server.close(); await mongoose.connection.close(); }); diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 880c4c87..5b9d0e92 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -390,9 +390,9 @@ describe('Users Routes', () => { }); afterAll(async () => { - db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection - db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection - server.close(); - await mongoose.connection.close(); + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + await server.close(); + mongoose.connection.close(); }); }); \ No newline at end of file diff --git a/models/User.js b/models/User.js index 8a758c61..d24c3023 100644 --- a/models/User.js +++ b/models/User.js @@ -74,7 +74,7 @@ const userSchema = new Schema({ password: String, joinedAt: Date, resetAttempts: [Date], - modifiedAt: Date + modifiedAt: Date, subscriptions: [{ type: Schema.Types.ObjectId, ref: "courses" }] }); From d8eb0500f574121780e413cb4dced41daadcd59a Mon Sep 17 00:00:00 2001 From: Jatewo Date: Fri, 13 Oct 2023 14:56:54 +0200 Subject: [PATCH 116/129] fix(test): fixed some errors in signupRoute test --- __tests__/routes/signupRoutes.spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.spec.js index 370117ea..f011c12e 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.spec.js @@ -45,13 +45,14 @@ describe('Signup User route', () => { .expect(201); // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ email: input.email }); + const user = await db.collection('users').findOne({ email: userInput.email }); expect(user).toBeDefined(); - expect(user.email).toBe(input.email); + expect(user.email).toBe(userInput.email); }); it('Returns error if email is missing', async () => { + userInput.email = ''; const response = await request(`http://localhost:${PORT}`) .post('/api/signup/user') .send(userInput) From dd2993f79f03c7d11dd668e86640fd8e6298c357 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Sun, 15 Oct 2023 19:55:20 +0200 Subject: [PATCH 117/129] feat(api): Added routes for getting courses Changes made: - Created file for getting fake courses (for testing) - Removed leftover console.logs - Reworked requireLogin to check that the right user is accessing user specific content - Added adminOnly middleware for routes only meant to be reachable for admins (note that this is a placeholder and should not be considered as a finished feature) - Added tests for middleware - Added route for getting all courses (adminOnly) - Added route for getting all courses made by a specific content creator - Added tests for routes - Updated some old tests for the reworked requireLogin NOTE: Commented out some old tests for a 204: No content result when trying to access a non-existent user. Right now a 401: Unauthorized will returned. We need to discuss how we want to handle this. --- __tests__/fixtures/fakeCourses.js | 72 +++++++++ __tests__/middleware/adminOnly.test.js | 68 +++++++++ __tests__/middleware/requireLogin.spec.js | 2 +- __tests__/routes/courseRoutes.spec.js | 6 +- __tests__/routes/courseRoutes.test.js | 169 ++++++++++++++++++++++ __tests__/routes/userRoutes.spec.js | 64 ++++---- middlewares/adminOnly.js | 16 ++ middlewares/requireLogin.js | 29 ++-- routes/courseRoutes.js | 20 ++- routes/userRoutes.js | 2 - 10 files changed, 393 insertions(+), 55 deletions(-) create mode 100644 __tests__/fixtures/fakeCourses.js create mode 100644 __tests__/middleware/adminOnly.test.js create mode 100644 __tests__/routes/courseRoutes.test.js create mode 100644 middlewares/adminOnly.js diff --git a/__tests__/fixtures/fakeCourses.js b/__tests__/fixtures/fakeCourses.js new file mode 100644 index 00000000..908b3105 --- /dev/null +++ b/__tests__/fixtures/fakeCourses.js @@ -0,0 +1,72 @@ +const courses = [ + { + "title": 'fakeCourse1', + "description": 'fakeCourse1 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "category": 'mathematics', + "published": false, + "sections": [], + "creator": [], + "difficulty": 1, + "time": 1, + "rating": 5, + }, + { + "title": 'fakeCourse2', + "description": 'fakeCourse2 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "category": 'language', + "published": false, + "sections": [], + "creator": ['1234567891011'], + "difficulty": 1, + "time": 1, + "rating": 5, + }, + { + "title": 'fakeCourse3', + "description": 'fakeCourse3 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "category": 'programming', + "published": true, + "sections": [], + "creator": [], + "difficulty": 2, + "time": 3, + "rating": 5, + }, + { + "title": 'fakeCourse4', + "description": 'fakeCourse4 description', + "dateCreated": new Date().toJSON(), + "dateUpdated": new Date().toJSON(), + "category": 'music', + "published": true, + "sections": [], + "creator": ['1234567891011'], + "difficulty": 6, + "time": 2, + "rating": 3, + } +]; + +/** + * @returns {Array} Array of fake courses + */ +function getFakeCourses() { + return courses; +} + +/** + * @param {Number} creatorId + * @returns {Array} Array of fake courses created by the creator with the given id + */ +function getFakeCoursesByCreator(creatorId) { + const res = courses.filter(course => course.creator.includes(creatorId)); + return res; +} + +module.exports = { getFakeCourses, getFakeCoursesByCreator }; \ No newline at end of file diff --git a/__tests__/middleware/adminOnly.test.js b/__tests__/middleware/adminOnly.test.js new file mode 100644 index 00000000..567675fd --- /dev/null +++ b/__tests__/middleware/adminOnly.test.js @@ -0,0 +1,68 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../../routes/courseRoutes'); // Import your router file here +const connectDb = require('../fixtures/db'); +const { signAccessToken } = require('../../helpers/token'); +const mongoose = require('mongoose'); + +const app = express(); +app.use(express.json()); +app.use('/api', router); // Mount the router under '/api' path + +// Start the Express app on a specific port for testing +const PORT = 5022; // Choose a port for testing +const server = app.listen(PORT, () => { + console.log(`Express server is running on port ${PORT}`); +}); + +// Mocked token secret +const TOKEN_SECRET = 'test'; + +// Mock token secret +jest.mock('../../config/keys', () => { + return { + TOKEN_SECRET + }; +}); + +describe('Admin token verify', () => { + let db; + + beforeAll(done => { + done(); + db = connectDb(); // Connect to the database + }); + + it('Return an error if no valid admin token is present on private route', async () => { + const token = 'ImAnInvalidToken'; + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', token) + .expect(401); + + expect(response.body.error).toBeDefined(); + }); + + it('Return success if an admin token is valid on a private route', async () => { + const token = signAccessToken({ id: 'srdfet784y2uioejqr' }); + + // mock that token is valid + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', token) + .expect(200); + }); + + it('Test for non-algorithm attack', async () => { + const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.' + btoa(`{"id":1,"iat":${'' + Date.now()},"exp":999999999999}`) + '.'; + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', token) + .expect(401); + }); + + afterAll(async () => { + server.close(); + await mongoose.connection.close(); + }); +}); \ No newline at end of file diff --git a/__tests__/middleware/requireLogin.spec.js b/__tests__/middleware/requireLogin.spec.js index b23eb508..29f359bc 100644 --- a/__tests__/middleware/requireLogin.spec.js +++ b/__tests__/middleware/requireLogin.spec.js @@ -60,7 +60,7 @@ describe('JWT verify', () => { }); afterAll(async () => { - server.close(); + await server.close(); await mongoose.connection.close(); }); }); \ No newline at end of file diff --git a/__tests__/routes/courseRoutes.spec.js b/__tests__/routes/courseRoutes.spec.js index 2d0a3226..139156dc 100644 --- a/__tests__/routes/courseRoutes.spec.js +++ b/__tests__/routes/courseRoutes.spec.js @@ -18,9 +18,7 @@ app.use('/api/courses', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5021; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Create a fake user, course and section let fakeUser = makeFakeUser(); @@ -266,8 +264,6 @@ describe('Course Routes', () => { .post('/api/courses/' + courseId + '/subscribe') .send({ user_id: userId }); - console.log(response.body.subscriptions.find((element) => element == courseId)) - expect(response.status).toBe(200); expect(response.body).toBeInstanceOf(Object); expect(response.body.subscriptions.find((element) => element == courseId)); diff --git a/__tests__/routes/courseRoutes.test.js b/__tests__/routes/courseRoutes.test.js new file mode 100644 index 00000000..764631cb --- /dev/null +++ b/__tests__/routes/courseRoutes.test.js @@ -0,0 +1,169 @@ +const request = require('supertest'); +const express = require('express'); +const router = require('../../routes/courseRoutes'); // Import your router file here +const connectDb = require('../../__tests__/fixtures/db'); +const makeFakeUser = require('../../__tests__/fixtures/fakeUser'); +const { getFakeCourses, getFakeCoursesByCreator } = require('../../__tests__/fixtures/fakeCourses'); +const mongoose = require('mongoose'); +const { signAccessToken } = require('../../helpers/token'); +const errorCodes = require('../../helpers/errorCodes') + +const app = express(); +app.use(express.json()); +app.use('/api', router); // Add your router to the Express app + +// Mock Google OAuth2 clientID +jest.mock('../../config/keys', () => { + return { + GOOGLE_CLIENT_ID: 'test', + TOKEN_SECRET: 'test', + }; +}); + +// Start the Express app on a specific port for testing +const PORT = 5022; // Choose a port for testing +const ADMIN_ID = 'srdfet784y2uioejqr'; +const server = app.listen(PORT, () => { + console.log(`Express server is running on port ${PORT}`); +}); + +let fakeUser = makeFakeUser(); +const fakeCourses = getFakeCourses(); + +describe('Get all courses for user route', () => { + + let db; // Store the database connection + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + // Insert the fake user into the database + await db.collection('courses').insertMany(fakeCourses); + await db.collection('users').insertOne(fakeUser); + fakeUser = await db.collection('users').findOne({ email: fakeUser.email }); + }); + + it('Returns courses made by a given user', async () => { + + const courses = getFakeCoursesByCreator(fakeUser._id); + // Send a get request to the courses endpoint + const res = await request(`http://localhost:${PORT}`) + .get(`/api/courses/creator/${fakeUser._id}`) + .set('token', signAccessToken({ id: fakeUser._id })); + expect(res.statusCode).toEqual(200); + // Verify response body + const result = res.body; + + let i = 0; + // Verify that the response body contains the correct data + if (result.length > 0) { + res.body.forEach(course => { + expect(course).toMatchObject({ + _id: expect.any(String), + title: courses[i].title, + description: courses[i].description, + dateCreated: expect.any(String), + dateUpdated: expect.any(String), + category: courses[i].category, + published: courses[i].published, + sections: courses[i].sections, + creator: courses[i].creator, + difficulty: courses[i].difficulty, + time: courses[i].time, + rating: courses[i].rating, + }); + i++; + }); + } else { + expect(result).toStrictEqual(courses) + } + }); + + it('Returns error 401 if user is not authorized to access', async () => { + + // Send a get request to the courses endpoint + const res = await request(`http://localhost:${PORT}`) + .get(`/api/courses/creator/${fakeUser._id}`) + .set('token', signAccessToken({ id: 'notAuthorized' })); + expect(res.statusCode).toEqual(401); + // Verify response body + const result = res.body; + + expect(result.error).toStrictEqual(errorCodes['E0002']); + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + }); + +}); + +describe('Get all courses route', () => { + + let db; // Store the database connection + + beforeAll(async () => { + db = await connectDb(); // Connect to the database + + // Insert the fake user into the database + await db.collection('courses').insertMany(fakeCourses); + }); + + it('Returns courses', async () => { + + // Send a get request to the courses endpoint + + const res = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', signAccessToken({ id: ADMIN_ID })); + expect(res.statusCode).toEqual(200); + let i = 0; + res.body.forEach(course => { + expect(course).toMatchObject({ + _id: expect.any(String), + title: fakeCourses[i].title, + description: fakeCourses[i].description, + dateCreated: expect.any(String), + dateUpdated: expect.any(String), + category: fakeCourses[i].category, + published: fakeCourses[i].published, + sections: fakeCourses[i].sections, + creator: fakeCourses[i].creator, + difficulty: fakeCourses[i].difficulty, + time: fakeCourses[i].time, + rating: fakeCourses[i].rating, + }); + i++; + }); + }); + + it('Returns error 401 (E0002) if user is not authorized to access', async () => { + + let unauthorizedUser = makeFakeUser(); + unauthorizedUser.email = 'email@email.com'; // Avoiding duplicate error for email + + await db.collection('users').insertOne(unauthorizedUser); + const unauthorizedUserInDB = await db.collection('users').findOne({ email: unauthorizedUser.email }); + + // Send a get request to the courses endpoint + const res = await request(`http://localhost:${PORT}`) + .get(`/api/courses/creator/${fakeUser._id}`) + .set('token', signAccessToken({ id: unauthorizedUserInDB._id })); + expect(res.statusCode).toBe(401); + // Verify response body + const result = res.body; + + expect(result.error).toStrictEqual(errorCodes['E0002']); + }); + + afterAll(async () => { + await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection + await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection + }); +}); + +afterAll(async () => { + server.close(); + await mongoose.connection.close(); +}); \ No newline at end of file diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.spec.js index 5b9d0e92..a69bb758 100644 --- a/__tests__/routes/userRoutes.spec.js +++ b/__tests__/routes/userRoutes.spec.js @@ -4,9 +4,8 @@ const router = require('../../routes/userRoutes'); const connectDb = require('../fixtures/db'); const makeFakeUser = require('../fixtures/fakeUser'); const makeFakeCourse = require('../fixtures/fakeCourse'); -const { signAccessToken } = require('../../helpers/token'); +const { signAccessToken, verify } = require('../../helpers/token'); const mongoose = require('mongoose'); - const app = express(); app.use(express.json()); app.use('/api/users', router); // Mount the router under '/api' path @@ -24,18 +23,17 @@ let fakeCourse = makeFakeCourse(); // Mock token secret jest.mock('../../config/keys', () => { return { - TOKEN_SECRET + TOKEN_SECRET: TOKEN_SECRET, }; }); describe('Users Routes', () => { - let token, fakeUser, db; + let token, fakeUser, db, actualUser; beforeAll(async () => { db = await connectDb(); // Connect to the database - token = signAccessToken({ id: 1 }); fakeUser = makeFakeUser(); await db.collection('courses').insertOne(fakeCourse); @@ -44,11 +42,13 @@ describe('Users Routes', () => { beforeEach(async () => { // Insert the fake user into the database before each test await db.collection('users').insertOne(fakeUser); + actualUser = await db.collection('users').findOne({ email: fakeUser.email }) + token = signAccessToken({ id: actualUser._id }) }); afterEach(async () => { // Remove the user from the database after each test - await db.collection('users').deleteOne({ _id: fakeUser._id }); + await db.collection('users').deleteOne({ _id: actualUser._id }); }); describe('Update User Email Route', () => { @@ -56,35 +56,35 @@ describe('Users Routes', () => { it('deletes a user successfully', async () => { // Delete the user using the API await request(`http://localhost:${PORT}`) - .delete(`/api/users/delete/${fakeUser._id}`) + .delete(`/api/users/delete/${actualUser._id}`) .set('token', token) // Include the token in the request headers .expect(200); // Verify that the user was deleted from the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); + const user = await db.collection('users').findOne({ _id: actualUser._id }); expect(user).toBeNull(); }); - it('handles user not found error for delete', async () => { + /*it('handles user not found error for delete', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) .delete(`/api/users/delete/${nonExistentUserId}`) .set('token', token) // Include the token in the request headers .expect(204); - }); + });*/ it('updates user email successfully', async () => { const newEmail = 'newemail@example.com'; await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ email: newEmail }) .expect(200); // Expecting a 200 OK response // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); + const user = await db.collection('users').findOne({ _id: actualUser._id }); expect(user).toBeDefined(); expect(user.email).toBe(newEmail); }); @@ -93,7 +93,7 @@ describe('Users Routes', () => { const newEmail = fakeUser.email; const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ email: newEmail }) .expect(400); // Expecting a 400 Bad Request response @@ -101,7 +101,7 @@ describe('Users Routes', () => { expect(response.body.error.code).toBe('E0201'); }); - it('handles user not found error for update-email', async () => { + /*it('handles user not found error for update-email', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; @@ -110,24 +110,24 @@ describe('Users Routes', () => { .set('token', token) // Include the token in the request headers .send({ email: newEmail }) .expect(204); // Expecting a 204 No Content response for user not found - }); + });*/ it('updates user first name successfully', async () => { const newFirstName = 'newFirstName'; await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ firstName: newFirstName }) .expect(200); // Expecting a 200 OK response // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); + const user = await db.collection('users').findOne({ _id: actualUser._id }); expect(user).toBeDefined(); expect(user.firstName).toBe(newFirstName); }); - it('handles user not found error for update-first-name', async () => { + /*it('handles user not found error for update-first-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) @@ -135,24 +135,24 @@ describe('Users Routes', () => { .set('token', token) // Include the token in the request headers .send({ newFirstName: 'NewFirstName' }) .expect(204); - }); + });*/ it('updates user last name successfully', async () => { const newLastName = 'newLastName'; await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ lastName: newLastName }) .expect(200); // Expecting a 200 OK response // Verify that the user was saved in the database - const user = await db.collection('users').findOne({ _id: fakeUser._id }); + const user = await db.collection('users').findOne({ _id: actualUser._id }); expect(user).toBeDefined(); expect(user.lastName).toBe(newLastName); }); - it('handles user not found error for update-last-name', async () => { + /*it('handles user not found error for update-last-name', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); await request(`http://localhost:${PORT}`) @@ -160,14 +160,14 @@ describe('Users Routes', () => { .set('token', token) // Include the token in the request headers .send({ newLastName: 'NewLastName' }) .expect(204); - }); + });*/ it('updates user fields successfully', async () => { const newEmail = 'newemail@example.com'; const newFirstName = 'Jane'; await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ email: newEmail, @@ -176,12 +176,12 @@ describe('Users Routes', () => { .expect(200); // Expecting a 200 OK response // Verify that the user was updated in the database - const updatedUser = await db.collection('users').findOne({ _id: fakeUser._id }); + const updatedUser = await db.collection('users').findOne({ _id: actualUser._id }); expect(updatedUser.email).toBe(newEmail); expect(updatedUser.firstName).toBe(newFirstName); }); - it('handles user not found error', async () => { + /*it('handles user not found error', async () => { const nonExistentUserId = new mongoose.Types.ObjectId(); const newEmail = 'newemail@example.com'; @@ -192,13 +192,13 @@ describe('Users Routes', () => { email: newEmail }) .expect(204); // Expecting a 204 No Content response for user not found - }); + });*/ it('handles validation errors for email', async () => { const invalidEmail = 'invalidemail'; // Invalid email format const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ email: invalidEmail @@ -212,7 +212,7 @@ describe('Users Routes', () => { const invalidFirstName = 'AASD!==#ยค("DSN:_;>:'; // Invalid email format const response = await request(`http://localhost:${PORT}`) - .patch('/api/users/' + fakeUser._id) + .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ firstName: invalidFirstName @@ -239,7 +239,7 @@ describe('Users Routes', () => { { _id: userId }, // Convert userId to ObjectId if needed { $push: { subscriptions: courseId } }, { returnDocument: 'after' } // 'after' returns the updated document - ); + ) const updatedUser = result.value; @@ -269,7 +269,7 @@ describe('Users Routes', () => { }); - it('should handle user not found error', async () => { + /*it('should handle user not found error', async () => { const course = await db.collection('courses').findOne({ title: 'test course' }); const courseId = course._id; @@ -284,7 +284,7 @@ describe('Users Routes', () => { expect(response.status).toBe(404); expect(response.body.error.code).toBe('E0004'); - }); + });*/ it('should handle invalid user id', async () => { diff --git a/middlewares/adminOnly.js b/middlewares/adminOnly.js new file mode 100644 index 00000000..fe5dac39 --- /dev/null +++ b/middlewares/adminOnly.js @@ -0,0 +1,16 @@ +const { verify } = require('../helpers/token'); + +const ADMIN_ID = 'srdfet784y2uioejqr' + +module.exports = (req, res, next) => { + try { + const claims = verify(req.headers.token ?? ''); + if(claims.id !== ADMIN_ID) { + return res.status(401).send({ error: 'You are not allowed to access this content!' }); + } + next(); + } catch { + // TODO: add updated error messages + return res.status(401).send({ error: 'You must be logged in to acces this content!' }); + } +}; \ No newline at end of file diff --git a/middlewares/requireLogin.js b/middlewares/requireLogin.js index 67a5f7ac..4cfb49aa 100644 --- a/middlewares/requireLogin.js +++ b/middlewares/requireLogin.js @@ -1,11 +1,22 @@ const { verify } = require('../helpers/token'); +const { User } = require('../models/User'); +const errorCodes = require('../helpers/errorCodes'); +const isValidId = require('../helpers/Id'); -module.exports = (req, res, next) => { - try { - const claims = verify(req.headers.token ?? ''); - next(); - } catch { - // TODO: add updated error messages - return res.status(401).send({ error: 'You must be logged in!' }); - } -}; \ No newline at end of file +module.exports = async (req, res, next) => { + + let claims; + try { + claims = verify(req.headers.token); + } catch { + return res.status(401).send({ error: errorCodes['E0001'] }); + } + + if (req.params.id) { + if (claims.id !== req.params.id || !claims.id) { + return res.status(401).send({ error: errorCodes['E0002'] }); + } + } + + next(); +} \ No newline at end of file diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index c87a8148..0b4855b6 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -4,6 +4,7 @@ const express = require('express'); const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); +const adminOnly = require("../middlewares/adminOnly"); // Models const { CourseModel } = require("../models/Courses"); @@ -20,6 +21,19 @@ const { IdentityStore } = require("aws-sdk"); /*** COURSE, SECTIONS AND EXERCISE ROUTES ***/ +// Get all courses +router.get('/courses', adminOnly, async (req, res) => { + const result = await CourseModel.find({}); + res.send(result); +}); + +// Get all courses for one user +router.get('/courses/creator/:id', requireLogin, async (req, res) => { + const id = req.params.id; // Get user id from request + const courses = await CourseModel.find({creator: id}); // Find courses for a specific user + + res.send(courses); // Send response +}); //Get all courses router.get('', async (req, res) => { @@ -64,8 +78,6 @@ router.get('/:id', async (req, res) => { res.send(course); } catch (error) { - - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } @@ -105,7 +117,6 @@ router.get('/:id/sections', async (req, res) => { res.send(sectionsList); } catch (error) { - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } @@ -135,7 +146,6 @@ router.get('/:courseId/sections/:sectionId', async (req, res) => { res.send(section); } catch (error) { - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } @@ -174,7 +184,6 @@ router.post('/:id/subscribe', async (req, res) => { res.send(user); } catch (error) { - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } @@ -210,7 +219,6 @@ router.post('/:id/unsubscribe', async (req, res) => { res.send(user) } catch (error) { - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } diff --git a/routes/userRoutes.js b/routes/userRoutes.js index ba4ef088..341cf23d 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -112,7 +112,6 @@ router.get('/:id/subscriptions', async (req, res) => { } catch (error) { // If the server could not be reached, return an error message - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } }); @@ -151,7 +150,6 @@ router.get('/subscriptions', async (req, res) => { } catch (error) { // If the server could not be reached, return an error message - console.log(error); return res.status(500).json({ 'error': errorCodes['E0003'] }); } }); From 52af068e39ce91abed0e6a0b0044c715f8ae512e Mon Sep 17 00:00:00 2001 From: Jatewo Date: Mon, 16 Oct 2023 09:52:53 +0200 Subject: [PATCH 118/129] fix: Updated course model --- models/Courses.js | 67 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/models/Courses.js b/models/Courses.js index 0a72162a..80451280 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -1,30 +1,59 @@ // Mongoose model class for Courses const mongoose = require('mongoose'); -const { component } = require('./Components'); const { Schema } = mongoose; +// Routes are sorted into COURSE - SECTION - COMPONENT each with ASCII art, within each functions are in order of CRUD +// NOTE Files do NOT delete from the backend yet, on the TODO as of 03/2022 // Class description const courseSchema = new Schema({ - title: String, - description: String, - dateCreated: Date, - dateUpdated: Date, - coverImg: component, - category: String, - published: Boolean, - difficulty: Number, - status: String, - rating: Number, - numOfSubscriptions: Number, + title: { + type: String, + required: [true, 'Title is required'], + }, + description: { + type: String, + required: [true, 'Description is required'], + }, + dateCreated: Date, + dateUpdated: Date, + coverImg: component, + category: { + type: String, + enum: ['Math', 'Science', 'Finance', 'Language', 'Sustainability', 'Other'], + }, + published: Boolean, + sections: [{ + type: Schema.Types.ObjectId, + ref: 'Component' + }], + creator: { + type: Schema.Types.ObjectId, + ref: 'User' + }, + difficulty: { + type: Number, + }, + status: { + type: String, + enum: ['draft', 'published', 'hidden'], + default: 'draft', + }, estimatedHours: Number, - sections: [{ type: Schema.Types.ObjectId, ref: "sections" }], - author: { type: Schema.Types.ObjectId, ref: "contentCreator" }, + rating: { + Number, + }, + numOfSubscriptions:{ + type: Number, + default: 0, + }, + sections: [{ + type: Schema.Types.ObjectId, ref: "sections" + }], }); -const CourseModel = mongoose.model( - "courses", - courseSchema -); +const CourseModel = mongoose.model('courses', courseSchema); // Create new collection called courses, using the courseScema + +module.exports = { CourseModel }; + -module.exports = { CourseModel } From cbb8d4956ab51c2b0991e0ce57d246bd78412ee8 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Mon, 16 Oct 2023 09:54:28 +0200 Subject: [PATCH 119/129] fix(model): added missing import in course model --- models/Courses.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/Courses.js b/models/Courses.js index 80451280..39fdc320 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -1,3 +1,5 @@ +const component = require('../models/Components') + // Mongoose model class for Courses const mongoose = require('mongoose'); const { Schema } = mongoose; From aadd64fc590a25b4b0d4e3e97e6cfbe0f21130da Mon Sep 17 00:00:00 2001 From: JackSMunn Date: Mon, 16 Oct 2023 10:45:01 +0200 Subject: [PATCH 120/129] fix: Sections field redundancy - Deleted one of the "sections" fields under courses model. --- models/Courses.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/models/Courses.js b/models/Courses.js index 39fdc320..05e3c5b6 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -25,10 +25,6 @@ const courseSchema = new Schema({ enum: ['Math', 'Science', 'Finance', 'Language', 'Sustainability', 'Other'], }, published: Boolean, - sections: [{ - type: Schema.Types.ObjectId, - ref: 'Component' - }], creator: { type: Schema.Types.ObjectId, ref: 'User' From ac13926b20bc67bd879193de7094aa388cd8a9b9 Mon Sep 17 00:00:00 2001 From: JackSMunn Date: Mon, 16 Oct 2023 10:55:57 +0200 Subject: [PATCH 121/129] fix(model): type of "creator" to contentcreator - Changed the model, creator was user, is now contentCreator --- models/Courses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Courses.js b/models/Courses.js index 05e3c5b6..2c73c4da 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -27,7 +27,7 @@ const courseSchema = new Schema({ published: Boolean, creator: { type: Schema.Types.ObjectId, - ref: 'User' + ref: 'contentCreator' }, difficulty: { type: Number, From 3ee4f003c7fb46d89d543e591a1befa18e863418 Mon Sep 17 00:00:00 2001 From: JackSMunn Date: Mon, 16 Oct 2023 11:18:11 +0200 Subject: [PATCH 122/129] fix(model): updated possible categories - Changed categories to the PO desired categories. --- models/Courses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Courses.js b/models/Courses.js index 2c73c4da..98a2d0e5 100644 --- a/models/Courses.js +++ b/models/Courses.js @@ -22,7 +22,7 @@ const courseSchema = new Schema({ coverImg: component, category: { type: String, - enum: ['Math', 'Science', 'Finance', 'Language', 'Sustainability', 'Other'], + enum: ['personal finance', 'health and workplace safety', 'sewing', 'electronics', 'other'], }, published: Boolean, creator: { From 3020bb022e3ce2e57ed1bf221b1542a97d123f6a Mon Sep 17 00:00:00 2001 From: JackSMunn Date: Mon, 16 Oct 2023 13:45:46 +0200 Subject: [PATCH 123/129] feat(api): added edit password functionality - Added errorcodes to errorcodes.js - Fixed wording of E0206 - Added password regex to patterns.js - Added validatePassword function to validation.js - Changed used errorcode in authRoutes.js - Added documentation to errocodes in userRoutes.js - Added functionality to update password in user patch endpoint. - Updated high cyclomatic complexity method, to a switch case to lower the CC. - Added function to ensure the values that user tries to update, is new. - Added more errorcodes to userRoutes.js --- helpers/errorCodes.js | 22 ++++++++++- helpers/patterns.js | 2 + helpers/validation.js | 17 +++++++++ routes/authRoutes.js | 5 ++- routes/userRoutes.js | 89 ++++++++++++++++++++++++++++--------------- 5 files changed, 101 insertions(+), 34 deletions(-) diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index 3fbe6bd0..17534269 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -92,7 +92,7 @@ module.exports = { }, E0206: { code: 'E0206', - message: 'Email must contain "@" and ".".' + message: "Email must contain '@' and '.'" }, E0207: { code: 'E0207', @@ -118,6 +118,14 @@ module.exports = { code: 'E0212', message: 'Password is required.' }, + E0213: { + code: 'E0213', + message: 'Password must be at least 8 characters.' + }, + E0214: { + code: 'E0214', + message: 'Password must contain at least one letter.' + }, // E03 - Logout errors E0301: { @@ -182,5 +190,15 @@ module.exports = { E0604: { code: 'E0604', message: 'Could not check users subscriptions' - } + }, + + // Model update errors: + E0701: { + code: 'E0701', + message: 'Attempted to update illegal field name' + }, + E0702: { + code: 'E0702', + message: 'Field value is identical to the current value.' + }, }; \ No newline at end of file diff --git a/helpers/patterns.js b/helpers/patterns.js index ca603ed8..42acdcac 100644 --- a/helpers/patterns.js +++ b/helpers/patterns.js @@ -1,6 +1,8 @@ const regexPatterns = { email: /^[a-z0-9!'#$%&*+\/=?^_`{|}~-]+(?:\.[a-z0-9!'#$%&*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-zA-Z]{2,}$/i, name: /^(\p{L}+[ -'])*\p{L}+$/u, + password: /^.*\p{L}.*$/u + }; module.exports = Object.freeze({ diff --git a/helpers/validation.js b/helpers/validation.js index 2f9d4f3e..5a9b2259 100644 --- a/helpers/validation.js +++ b/helpers/validation.js @@ -52,6 +52,22 @@ function validateName(input) { return true; } +function validatePassword (input) { + const passwordPattern = patterns.password; + + if (isMissing(input)) { + throw errorCodes['E0212']; // Password is required + } + if (input.length < 8) { + throw errorCodes['E0213']; // Password must be at least 8 characters + } + if (!(passwordPattern.test(input))) { + throw errorCodes['E0214']; // Password must contain at least one letter + } + + return true; +} + function isMissing(input) { return input === undefined || input === null || input === ''; } @@ -59,5 +75,6 @@ function isMissing(input) { module.exports = { validateEmail, validateName, + validatePassword, isMissing }; diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 6b57516c..4cf9efc3 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -43,9 +43,10 @@ router.post('/login', async (req, res) => { // Searching for a single user in the database, with the email provided in the request body const user = await User.findOne({ email: req.body.email}); // If email is found, compare the password provided in the request body with the password in the database + console.log(req.body) if (!user) { // Invalid email (email not found) - return res.status(401).json({ 'error': errorCodes['E0101']}); + return res.status(401).json({ 'error': errorCodes['E0004']}); } else { // If the email is found, compare the passwords @@ -99,7 +100,7 @@ router.post('/reset-password-request', async (req, res) => { } // If there are more than 2 attempts in the last hour, return error E0406 if(user.resetAttempts.length > 2) { - return res.status(400).json({ error: errorCodes['E0406'] }); + return res.status(400).json({ error: errorCodes['E0406'] }); // } user.resetAttempts.push(Date.now()); diff --git a/routes/userRoutes.js b/routes/userRoutes.js index ba4ef088..c791cc0f 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -1,9 +1,10 @@ const router = require('express').Router(); -const { validateEmail, validateName } = require('../helpers/validation'); +const { validateEmail, validateName, validatePassword } = require('../helpers/validation'); const errorCodes = require('../helpers/errorCodes'); const { User } = require('../models/User'); const { CourseModel } = require('../models/Courses'); const requireLogin = require('../middlewares/requireLogin'); +const { encrypt, compare } = require('../helpers/password'); router.delete('/delete/:id', requireLogin, async (req, res) => { try { @@ -12,20 +13,20 @@ router.delete('/delete/:id', requireLogin, async (req, res) => { const deletedUser = await User.findByIdAndDelete(id); if (!deletedUser) { - throw errorCodes['E0004']; + throw errorCodes['E0004']; // User not found } else { res.status(200); res.send(deletedUser) } } catch (error) { - if (error === errorCodes['E0004']) { + if (error === errorCodes['E0004']) { // User not found // Handle "user not found" error response here res.status(204); } else { res.status(400); } - + res.send({ error: error }); @@ -40,50 +41,78 @@ router.patch('/:id', requireLogin, async (req, res) => { const validFields = await validateFields(updateFields); + const user = await User.findById(id); + + if (!ensureNewValues(updateFields, user)) { + return res.status(400).send({ error: errorCodes['E0702'] }) + } + + if(updateFields.password) { + updateFields.password = encrypt(updateFields.password); + } + if (validFields) { - const updatedUser = await User.findByIdAndUpdate( - id, - { $set: updateFields }, - { new: true } // This ensures that the updated user document is returned - ); + const updatedUser = await User.findByIdAndUpdate( id, { $set: updateFields }, { new: true }) if (!updatedUser) { - throw errorCodes['E0004']; + throw errorCodes['E0004']; // User not found } res.status(200).send(updatedUser); } } catch (error) { - if (error === errorCodes['E0004']) { + if (error === errorCodes['E0004']) { // User not found // Handle "user not found" error response here - res.status(204); + res.status(204).send({ error: errorCodes['E0004'] }); // User not found } else { - res.status(400); + res.status(400).send({ error: error.message }); } - - res.send({ - error: error - }); } }); +/** + * Validates the fields to be updated dynamically + */ async function validateFields(fields) { const fieldEntries = Object.entries(fields); for (const [fieldName, fieldValue] of fieldEntries) { - if (fieldName === 'email') { - const emailValid = await validateEmail(fieldValue); - if (!emailValid) { - return false; - } - } else if (fieldName === 'firstName' || fieldName === 'lastName') { - const nameValid = await validateName(fieldValue); - if (!nameValid) { - return false; - } + switch (fieldName) { + case 'email': + if (!(await validateEmail(fieldValue))) { + return false; + } + break; + case 'firstName' || 'lastName': + if (!validateName(fieldValue)) { + return false; + } + break; + case 'password': + if (!validatePassword(fieldValue)) { + return false; + } + break; + // Add more cases for other fields if needed + default: + throw new Error('Illegal field name'); + } + } + return true; +} + +function ensureNewValues(newValues, oldValues) { + const newEntries = Object.entries(newValues); + + for (const [fieldName, fieldValue] of newEntries) { + if (fieldName === 'password' && compare(fieldValue, oldValues.password)) { + return false; + } else if (fieldValue === oldValues[fieldName]) { + return false; } } + return true; } @@ -102,7 +131,7 @@ router.get('/:id/subscriptions', async (req, res) => { // Handle "user not found" error response here return res.status(404).json({ 'error': errorCodes['E0004'] }); } - + const subscribedCourses = user.subscriptions; // Find courses based on the subscribed course IDs @@ -113,7 +142,7 @@ router.get('/:id/subscriptions', async (req, res) => { } catch (error) { // If the server could not be reached, return an error message console.log(error); - return res.status(500).json({ 'error': errorCodes['E0003'] }); + return res.status(500).json({ 'error': errorCodes['E0003'] }); // server could not be reached } }); @@ -152,7 +181,7 @@ router.get('/subscriptions', async (req, res) => { } catch (error) { // If the server could not be reached, return an error message console.log(error); - return res.status(500).json({ 'error': errorCodes['E0003'] }); + return res.status(500).json({ 'error': errorCodes['E0003'] }); // server could not be reached } }); From b4ae5757b954f2bf4180bb1f4eae9251d341d7a9 Mon Sep 17 00:00:00 2001 From: JackSMunn Date: Mon, 16 Oct 2023 14:05:59 +0200 Subject: [PATCH 124/129] fix(test): Changed name of test files - Changed courseRoutes.spec.js to courseRoutes.test.js - Changed signupRoutes.spec.js to signupRoutes.test.js - Changed userRoutes.spec.js to userRoutes.test.js - Changed errorcodes in userRoutes to category E08 instead of E07 --- .../{courseRoutes.spec.js => courseRoutes.test.js} | 2 +- .../{signupRoutes.spec.js => signupRoutes.test.js} | 4 ++-- .../routes/{userRoutes.spec.js => userRoutes.test.js} | 0 helpers/errorCodes.js | 9 +++++---- routes/authRoutes.js | 2 +- routes/userRoutes.js | 6 +++--- 6 files changed, 12 insertions(+), 11 deletions(-) rename __tests__/routes/{courseRoutes.spec.js => courseRoutes.test.js} (99%) rename __tests__/routes/{signupRoutes.spec.js => signupRoutes.test.js} (97%) rename __tests__/routes/{userRoutes.spec.js => userRoutes.test.js} (100%) diff --git a/__tests__/routes/courseRoutes.spec.js b/__tests__/routes/courseRoutes.test.js similarity index 99% rename from __tests__/routes/courseRoutes.spec.js rename to __tests__/routes/courseRoutes.test.js index 2d0a3226..d5020340 100644 --- a/__tests__/routes/courseRoutes.spec.js +++ b/__tests__/routes/courseRoutes.test.js @@ -2,7 +2,7 @@ const request = require('supertest'); const express = require('express'); const router = require('../../routes/courseRoutes'); const mongoose = require('mongoose'); -const connectDb = require('../../__tests__/fixtures/db'); +const connectDb = require('../fixtures/db'); const makeFakeUser = require('../fixtures/fakeUser'); const makeFakeCourse = require('../fixtures/fakeCourse'); const makeFakeSection = require('../fixtures/fakeSection'); diff --git a/__tests__/routes/signupRoutes.spec.js b/__tests__/routes/signupRoutes.test.js similarity index 97% rename from __tests__/routes/signupRoutes.spec.js rename to __tests__/routes/signupRoutes.test.js index 5fb2c999..4c1839d5 100644 --- a/__tests__/routes/signupRoutes.spec.js +++ b/__tests__/routes/signupRoutes.test.js @@ -1,8 +1,8 @@ const request = require('supertest'); const express = require('express'); const router = require('../../routes/signupRoutes'); -const connectDb = require('../../__tests__/fixtures/db'); -const makeFakeUser = require('../../__tests__/fixtures/fakeUser'); +const connectDb = require('../fixtures/db'); +const makeFakeUser = require('../fixtures/fakeUser'); const mongoose = require('mongoose'); diff --git a/__tests__/routes/userRoutes.spec.js b/__tests__/routes/userRoutes.test.js similarity index 100% rename from __tests__/routes/userRoutes.spec.js rename to __tests__/routes/userRoutes.test.js diff --git a/helpers/errorCodes.js b/helpers/errorCodes.js index 17534269..de6c2cbf 100644 --- a/helpers/errorCodes.js +++ b/helpers/errorCodes.js @@ -193,12 +193,13 @@ module.exports = { }, // Model update errors: - E0701: { - code: 'E0701', + E0801: { + code: 'E0801', message: 'Attempted to update illegal field name' }, - E0702: { - code: 'E0702', + E0802: { + code: 'E0802', message: 'Field value is identical to the current value.' }, + }; \ No newline at end of file diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 4cf9efc3..0a1b295a 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -100,7 +100,7 @@ router.post('/reset-password-request', async (req, res) => { } // If there are more than 2 attempts in the last hour, return error E0406 if(user.resetAttempts.length > 2) { - return res.status(400).json({ error: errorCodes['E0406'] }); // + return res.status(400).json({ error: errorCodes['E0406'] }); } user.resetAttempts.push(Date.now()); diff --git a/routes/userRoutes.js b/routes/userRoutes.js index c791cc0f..611f799f 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -44,7 +44,7 @@ router.patch('/:id', requireLogin, async (req, res) => { const user = await User.findById(id); if (!ensureNewValues(updateFields, user)) { - return res.status(400).send({ error: errorCodes['E0702'] }) + return res.status(400).send({ error: errorCodes['E0802'] }) } if(updateFields.password) { @@ -58,7 +58,7 @@ router.patch('/:id', requireLogin, async (req, res) => { throw errorCodes['E0004']; // User not found } - res.status(200).send(updatedUser); + res.status(200).send({ message: 'success' }); } } catch (error) { @@ -96,7 +96,7 @@ async function validateFields(fields) { break; // Add more cases for other fields if needed default: - throw new Error('Illegal field name'); + throw errorCodes['E0801']; } } return true; From babfacc327d45772707da429cff94780746deeb2 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Mon, 16 Oct 2023 15:22:08 +0200 Subject: [PATCH 125/129] test(api): Added test of user PATCH route Changes made: - Updated test for authRoutes - Added tests for user PATCH route - Fixed small mistakes found in tests in userRoutes - Added update to modifiedAt at user PATCH route --- __tests__/routes/authRoutes.test.js | 18 +---- __tests__/routes/userRoutes.test.js | 110 +++++++++++++++++++++++++++- routes/userRoutes.js | 7 +- 3 files changed, 113 insertions(+), 22 deletions(-) diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index b5140aea..deebc10a 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -58,23 +58,7 @@ describe('Login User route', () => { .expect(401); // Verify the response body - expect(response.body.error.code).toBe('E0101'); - }); - - it('Returns error if user is not found', async () => { - const nonExistingUser = { - email: 'iDontExist@test.dk', - password: encrypt('12345678') - }; - - // Send a Post request to the login endpoint - const response = await request(`http://localhost:${PORT}`) - .post('/api/auth/login') - .send(nonExistingUser) - .expect(401); - - // Verify the response body - expect(response.body.error.code).toBe('E0101'); + expect(response.body.error.code).toBe('E0004'); }); diff --git a/__tests__/routes/userRoutes.test.js b/__tests__/routes/userRoutes.test.js index a69bb758..27252ce9 100644 --- a/__tests__/routes/userRoutes.test.js +++ b/__tests__/routes/userRoutes.test.js @@ -140,7 +140,7 @@ describe('Users Routes', () => { it('updates user last name successfully', async () => { const newLastName = 'newLastName'; - await request(`http://localhost:${PORT}`) + const res = await request(`http://localhost:${PORT}`) .patch('/api/users/' + actualUser._id) .set('token', token) // Include the token in the request headers .send({ lastName: newLastName }) @@ -386,13 +386,119 @@ describe('Users Routes', () => { expect(response.status).toBe(500); expect(response.body.error.code).toBe('E0003'); }); + }); + + describe('User PATCH route', () => { + + it('should change user credentials', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({ + password: 'newPassword', + email: 'newEmail@email.com', + firstName: 'newFirstName', + lastName: 'newLastName', + }) + .expect(200); // Expecting a 200 OK response + + const updatedUser = await db.collection('users').findOne({ _id: user._id }); + + expect(updatedUser).toMatchObject({ + firstName: 'newFirstName', + lastName: 'newLastName', + email: 'newEmail@email.com', + password: expect.any(String), + joinedAt: user.joinedAt, + resetAttempts: user.resetAttempts, + modifiedAt: expect.any(Date), + subscriptions: user.subscriptions, + }); + }); + + it('return error if password is not atleast 8 characters long', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({ password: 'newpass'}) + .expect(400); // Expecting a 400 response + + expect(res.body.error.code).toBe('E0213'); + }); + + it('return error if password does not contain atleast one letter', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({ password: '12345678'}) + .expect(400); // Expecting a 400 response + + expect(res.body.error.code).toBe('E0214'); + }); + + it('return error if there is an attempt to update an illegal field name', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({ createdAt: Date.now() }) + .expect(400); // Expecting a 400 response + + expect(res.body.error.code).toBe('E0801'); + }); + + it('return succesful updated object with new modifiedAt value ', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({ email: 'hej@hej.dk' + }) + .expect(200); // Expecting a 200 OK response + + const updatedUser = await db.collection('users').findOne({ _id: user._id }); + expect(updatedUser.modifiedAt).not.toBe(user.modifiedAt); }); + it('return error if email you try to PATCH is identical', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({email: fakeUser.email}) + .expect(400); // Expecting a 400 response + + expect(res.body.error.code).toBe('E0201'); +}); + +it('return error if firstName you try to PATCH is identical', async () => { + const user = await db.collection('users').findOne({ email: fakeUser.email }); + + const res = await request(`http://localhost:${PORT}`) + .patch('/api/users/' + user._id) + .set('token', token) // Include the token in the request headers + .send({firstName: fakeUser.firstName}) + .expect(400); // Expecting a 400 response + + expect(res.body.error.code).toBe('E0802'); +}); + + +}); afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection - await server.close(); + server.close(); mongoose.connection.close(); }); }); \ No newline at end of file diff --git a/routes/userRoutes.js b/routes/userRoutes.js index a109a80b..c7f9e1c9 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -52,7 +52,7 @@ router.patch('/:id', requireLogin, async (req, res) => { } if (validFields) { - const updatedUser = await User.findByIdAndUpdate( id, { $set: updateFields }, { new: true }) + const updatedUser = await User.findByIdAndUpdate( id, { $set: updateFields, modifiedAt: Date.now() }, { new: true }) if (!updatedUser) { throw errorCodes['E0004']; // User not found @@ -66,7 +66,7 @@ router.patch('/:id', requireLogin, async (req, res) => { // Handle "user not found" error response here res.status(204).send({ error: errorCodes['E0004'] }); // User not found } else { - res.status(400).send({ error: error.message }); + res.status(400).send({ error: error }); } } }); @@ -84,7 +84,8 @@ async function validateFields(fields) { return false; } break; - case 'firstName' || 'lastName': + case 'firstName': + case 'lastName': if (!validateName(fieldValue)) { return false; } From 29c2185da78364ac456a8d9e1f60c48537975bd6 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Tue, 17 Oct 2023 12:48:28 +0200 Subject: [PATCH 126/129] fix(test): Fixed some duplicated and deprecated tests - Reworked adminOnly tests to make use of a test route - Deleted duplicated tests for courseRoutes - Removed /courses from within courseRoutes - Made a test route for adminOnly --- __tests__/middleware/adminOnly.test.js | 17 +++++++------- __tests__/routes/courseRoutes.spec.js | 31 -------------------------- __tests__/routes/courseRoutes.test.js | 15 ++++++++++++- routes/authRoutes.js | 1 - routes/courseRoutes.js | 12 ++++------ routes/testRoutes.js | 5 +++++ 6 files changed, 31 insertions(+), 50 deletions(-) diff --git a/__tests__/middleware/adminOnly.test.js b/__tests__/middleware/adminOnly.test.js index 567675fd..a737213b 100644 --- a/__tests__/middleware/adminOnly.test.js +++ b/__tests__/middleware/adminOnly.test.js @@ -1,19 +1,17 @@ const request = require('supertest'); const express = require('express'); -const router = require('../../routes/courseRoutes'); // Import your router file here +const router = require('../../routes/testRoutes'); // Import your router file here const connectDb = require('../fixtures/db'); const { signAccessToken } = require('../../helpers/token'); const mongoose = require('mongoose'); const app = express(); app.use(express.json()); -app.use('/api', router); // Mount the router under '/api' path +app.use('/api/test', router); // Mount the router under '/api' path // Start the Express app on a specific port for testing const PORT = 5022; // Choose a port for testing -const server = app.listen(PORT, () => { - console.log(`Express server is running on port ${PORT}`); -}); +const server = app.listen(PORT); // Mocked token secret const TOKEN_SECRET = 'test'; @@ -36,9 +34,10 @@ describe('Admin token verify', () => { it('Return an error if no valid admin token is present on private route', async () => { const token = 'ImAnInvalidToken'; const response = await request(`http://localhost:${PORT}`) - .get('/api/courses') + .get('/api/test/adminOnly') .set('token', token) - .expect(401); + + console.log(response.body.error) expect(response.body.error).toBeDefined(); }); @@ -48,7 +47,7 @@ describe('Admin token verify', () => { // mock that token is valid const response = await request(`http://localhost:${PORT}`) - .get('/api/courses') + .get('/api/test/adminOnly') .set('token', token) .expect(200); }); @@ -56,7 +55,7 @@ describe('Admin token verify', () => { it('Test for non-algorithm attack', async () => { const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.' + btoa(`{"id":1,"iat":${'' + Date.now()},"exp":999999999999}`) + '.'; const response = await request(`http://localhost:${PORT}`) - .get('/api/courses') + .get('/api/test/adminOnly') .set('token', token) .expect(401); }); diff --git a/__tests__/routes/courseRoutes.spec.js b/__tests__/routes/courseRoutes.spec.js index 139156dc..e9b8d328 100644 --- a/__tests__/routes/courseRoutes.spec.js +++ b/__tests__/routes/courseRoutes.spec.js @@ -39,19 +39,6 @@ describe('Course Routes', () => { }); - describe('GET /courses', () => { - it('should get all courses', async () => { - - const response = await request(`http://localhost:${PORT}`) - .get('/api/courses'); - expect(response.status).toBe(200); - expect(response.body).toBeInstanceOf(Array); - - // error handling for when there is no courses can be found in the bottom - }); - - }); - describe('GET /courses/:id', () => { @@ -476,24 +463,6 @@ describe('Course Routes', () => { }); }); - describe('GET /courses, error handling', () => { - - it('should handle no courses not found', async () => { - - // delete all courses - await db.collection('courses').deleteMany({}); - - // send request with no courses in db - const response = await request(`http://localhost:${PORT}`) - .get('/api/courses'); - - expect(response.status).toBe(404); - expect(response.body.error.code).toBe('E0005'); - }); - - }); - - afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection diff --git a/__tests__/routes/courseRoutes.test.js b/__tests__/routes/courseRoutes.test.js index 764631cb..a35463e4 100644 --- a/__tests__/routes/courseRoutes.test.js +++ b/__tests__/routes/courseRoutes.test.js @@ -10,7 +10,7 @@ const errorCodes = require('../../helpers/errorCodes') const app = express(); app.use(express.json()); -app.use('/api', router); // Add your router to the Express app +app.use('/api/courses', router); // Add your router to the Express app // Mock Google OAuth2 clientID jest.mock('../../config/keys', () => { @@ -157,6 +157,19 @@ describe('Get all courses route', () => { expect(result.error).toStrictEqual(errorCodes['E0002']); }); + it('returns error 404 if no courses are found', async () => { + + // delete all courses + await db.collection('courses').deleteMany({}); + + // send request with no courses in db + const response = await request(`http://localhost:${PORT}`) + .get('/api/courses') + .set('token', signAccessToken({ id: ADMIN_ID })) + .expect(404) + expect(response.body.error.code).toBe('E0005'); + }); + afterAll(async () => { await db.collection('users').deleteMany({}); // Delete all documents in the 'users' collection await db.collection('courses').deleteMany({}); // Delete all documents in the 'courses' collection diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 6b57516c..346a6f84 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -6,7 +6,6 @@ const { authEndpointHandler } = require('../auth'); const { signAccessToken } = require('../helpers/token'); const { compare, encrypt } = require('../helpers/password'); const errorCodes = require('../helpers/errorCodes'); -const send = require('send'); const { sendResetPasswordEmail } = require('../helpers/email'); const { PasswordResetToken } = require('../models/PasswordResetToken'); diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 0b4855b6..0001b897 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -1,9 +1,5 @@ const router = require('express').Router(); const errorCodes = require('../helpers/errorCodes'); -const express = require('express'); -const app = express(); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); const adminOnly = require("../middlewares/adminOnly"); // Models @@ -22,13 +18,13 @@ const { IdentityStore } = require("aws-sdk"); /*** COURSE, SECTIONS AND EXERCISE ROUTES ***/ // Get all courses -router.get('/courses', adminOnly, async (req, res) => { +/*router.get('/', adminOnly, async (req, res) => { const result = await CourseModel.find({}); res.send(result); -}); +});*/ // Get all courses for one user -router.get('/courses/creator/:id', requireLogin, async (req, res) => { +router.get('/creator/:id', requireLogin, async (req, res) => { const id = req.params.id; // Get user id from request const courses = await CourseModel.find({creator: id}); // Find courses for a specific user @@ -36,7 +32,7 @@ router.get('/courses/creator/:id', requireLogin, async (req, res) => { }); //Get all courses -router.get('', async (req, res) => { +router.get('/', adminOnly, async (req, res) => { try { // find all courses in the database diff --git a/routes/testRoutes.js b/routes/testRoutes.js index ce8999b7..6ecbb055 100644 --- a/routes/testRoutes.js +++ b/routes/testRoutes.js @@ -1,4 +1,5 @@ const router = require('express').Router(); +const adminOnly = require('../middlewares/adminOnly'); const requireLogin = require('../middlewares/requireLogin'); // Route for testing JWT verification on private routes @@ -8,4 +9,8 @@ router.get('/require-jwt', requireLogin, (req, res) => { res.status(200).send(req.body); }); +router.get('/adminOnly', adminOnly, (req, res) => { + res.status(200).send(req.body); +}) + module.exports = router; \ No newline at end of file From 6f6674f33fa4b68c315531a6bb0a634b54d19323 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Wed, 18 Oct 2023 11:37:25 +0200 Subject: [PATCH 127/129] fix(test): Deleted some leftover commented-out code --- __tests__/routes/authRoutes.test.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/__tests__/routes/authRoutes.test.js b/__tests__/routes/authRoutes.test.js index deebc10a..6a071481 100644 --- a/__tests__/routes/authRoutes.test.js +++ b/__tests__/routes/authRoutes.test.js @@ -117,13 +117,6 @@ describe('Reset password request route', () => { it('Returns success if email is found and email is sent', async () => { sendResetPasswordEmail.mockImplementation(() => true); - /*jest.mock('../../helpers/email', () => { - return { - sendResetPasswordEmail: jest.fn((email, token) => { - return true; - }), - }; - });*/ const existingEmail = { email: fakeUser.email } const res = await request(`http://localhost:${PORT}`) .post('/api/auth/reset-password-request') From e8738ef3474740d0ccc6593d0f39341a629fdee8 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Wed, 18 Oct 2023 12:07:06 +0200 Subject: [PATCH 128/129] fix: updated to new error messages and fixed auth route types --- middlewares/adminOnly.js | 5 +++-- routes/authRoutes.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/middlewares/adminOnly.js b/middlewares/adminOnly.js index fe5dac39..cb928ce4 100644 --- a/middlewares/adminOnly.js +++ b/middlewares/adminOnly.js @@ -1,3 +1,4 @@ +const errorCodes = require('../helpers/errorCodes'); const { verify } = require('../helpers/token'); const ADMIN_ID = 'srdfet784y2uioejqr' @@ -6,11 +7,11 @@ module.exports = (req, res, next) => { try { const claims = verify(req.headers.token ?? ''); if(claims.id !== ADMIN_ID) { - return res.status(401).send({ error: 'You are not allowed to access this content!' }); + return res.status(401).send({ error: errorCodes['E0001'] }); } next(); } catch { // TODO: add updated error messages - return res.status(401).send({ error: 'You must be logged in to acces this content!' }); + return res.status(401).send({ error: errorCodes['E0002'] }); } }; \ No newline at end of file diff --git a/routes/authRoutes.js b/routes/authRoutes.js index 6f840777..e3997334 100644 --- a/routes/authRoutes.js +++ b/routes/authRoutes.js @@ -153,7 +153,7 @@ router.post('/reset-password-code', async (req, res) => { }); -router.put('/reset-password', async (req, res) => { +router.patch('/reset-password', async (req, res) => { const { email, token, newPassword } = req.body; const user = await User.findOne({ email: email }); @@ -183,7 +183,7 @@ router.put('/reset-password', async (req, res) => { }); // Logout simulation -router.get('/logout', (req, res) => { +router.post('/logout', (req, res) => { req.logout(); res.redirect('/'); }); From 4ed06ffc1fdd16aa64091fd79217b81fa5558a46 Mon Sep 17 00:00:00 2001 From: Jatewo Date: Wed, 18 Oct 2023 12:40:12 +0200 Subject: [PATCH 129/129] fix(api): deleted some leftover duplicate code --- routes/courseRoutes.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/routes/courseRoutes.js b/routes/courseRoutes.js index 0001b897..cdc4fff3 100644 --- a/routes/courseRoutes.js +++ b/routes/courseRoutes.js @@ -17,12 +17,6 @@ const { IdentityStore } = require("aws-sdk"); /*** COURSE, SECTIONS AND EXERCISE ROUTES ***/ -// Get all courses -/*router.get('/', adminOnly, async (req, res) => { - const result = await CourseModel.find({}); - res.send(result); -});*/ - // Get all courses for one user router.get('/creator/:id', requireLogin, async (req, res) => { const id = req.params.id; // Get user id from request