diff --git a/package.json b/package.json index e594e38e..052c835c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@ant-design/icons": "^4.7.0", "@aws-sdk/client-s3": "^3.178.0", "@aws-sdk/client-ses": "^3.180.0", + "@inquirer/prompts": "^3.0.4", "@next-auth/mongodb-adapter": "^1.0.4", "@types/escape-html": "^1.0.2", "@types/formidable": "^2.0.5", diff --git a/scripts/cli-util/get-hacker.ts b/scripts/cli-util/get-hacker.ts new file mode 100644 index 00000000..e474e152 --- /dev/null +++ b/scripts/cli-util/get-hacker.ts @@ -0,0 +1,110 @@ +import { input, select } from '@inquirer/prompts'; +import dbConnect from '../../middleware/database'; +import User from '../../models/user'; +import Event from '../../models/event'; +import Team from '../../models/team'; +import { promptAction } from '../dev-cli'; +import { UserData, EventData, TeamData } from '../../types/database'; + +export const handleGetHacker = async () => { + const hackerEmail = await input({ + message: 'Enter hacker email', + }); + + const user: UserData | null = await User.findOne({ email: hackerEmail }); + if (!user) { + console.log('Hacker not found'); + return promptAction(); + } + + const subAction1 = await select({ + message: 'Select an action to perform', + choices: [ + { + name: 'Get events attended', + value: 'get-events', + }, + { + name: 'Get team info', + value: 'get-team', + }, + { + name: 'Get application status', + value: 'get-application-status', + }, + { + name: 'Get document', + value: 'get-document', + }, + ], + }); + + // connect to db + await dbConnect(); + + switch (subAction1) { + case 'get-events': + await getEvents(user); + break; + case 'get-team': + await getTeam(user); + break; + case 'get-application-status': + await getApplicationStatus(user); + break; + case 'get-document': + await getDocument(user); + break; + default: + console.log('Invalid action'); + } +}; + +const getEvents = async (user: UserData) => { + const events = user.eventsAttended; + const size = events.length; + + if (size === 0) console.log('No events attended yet! :('); + + for (const eventId of events) { + const event: EventData | null = await Event.findOne({ _id: eventId }); + if (!event) { + console.log('Event not found'); + return promptAction(); + } else { + console.log(`Event name: ${event.name}`); + } + } + + console.log(''); + return promptAction(); +}; + +const getTeam = async (user: UserData) => { + const teamId = user.team; + const hackerTeam: TeamData | null = await Team.findOne({ _id: teamId }); + if (!hackerTeam) { + console.log('Team not found'); + return promptAction(); + } else { + console.log(`Team name: ${hackerTeam.name}`); + } + + console.log(''); + return promptAction(); +}; + +const getApplicationStatus = async (user: UserData) => { + console.log(`Application status: ${user.applicationStatus}`); + + console.log(''); + return promptAction(); +}; + +const getDocument = async (user: UserData) => { + console.log('User Document:'); + console.log(user); + + console.log(''); + return promptAction(); +}; diff --git a/scripts/cli-util/get-team.ts b/scripts/cli-util/get-team.ts new file mode 100644 index 00000000..98a0a15e --- /dev/null +++ b/scripts/cli-util/get-team.ts @@ -0,0 +1,86 @@ +import { input, select } from '@inquirer/prompts'; +import dbConnect from '../../middleware/database'; +import User from '../../models/user'; +import Team from '../../models/team'; +import { promptAction } from '../dev-cli'; +import { UserData, TeamData } from '../../types/database'; + +export const handleGetTeam = async () => { + const teamName = await input({ + message: 'Enter team name', + }); + + const team: TeamData | null = await Team.findOne({ name: teamName }); + if (!team) { + console.log('team not found'); + return promptAction(); + } + + const subAction1 = await select({ + message: 'Select an action to perform', + choices: [ + { + name: 'Get schedule', + value: 'get-schedule', + }, + { + name: 'Get members', + value: 'get-members', + }, + { + name: 'Get document', + value: 'get-document', + }, + ], + }); + + // connect to db + await dbConnect(); + + switch (subAction1) { + case 'get-schedule': + await getSchedule(team); + break; + case 'get-members': + await getMembers(team); + break; + case 'get-document': + await getDocument(team); + break; + default: + console.log('Invalid action'); + } +}; + +const getSchedule = async (team: TeamData) => { + console.log('IN PROGRESS'); + console.log(''); + return promptAction(); +}; + +const getMembers = async (team: TeamData) => { + const memberIds = team.members; + + let count = 1; + for (const memberId of memberIds) { + const member: UserData | null = await User.findOne({ _id: memberId }); + if (!member) { + console.log(`Member ${count++}:`); + console.log('Member not found'); + } else { + console.log(`Member ${count++}:`); + console.log(`Name: ${member.name}`); + console.log(`Email: ${member.email}`); + } + } + + return promptAction(); +}; + +const getDocument = async (team: TeamData) => { + console.log('Team Document:'); + console.log(team); + + console.log(''); + return promptAction(); +}; diff --git a/scripts/cli-util/modify-hacker.ts b/scripts/cli-util/modify-hacker.ts new file mode 100644 index 00000000..67e68312 --- /dev/null +++ b/scripts/cli-util/modify-hacker.ts @@ -0,0 +1,282 @@ +import { input, select } from '@inquirer/prompts'; +import Application from '../../models/application'; +import Team from '../../models/team'; +import User from '../../models/user'; +import { promptAction } from '../dev-cli'; +import { ApplicationStatus, TeamData, UserData } from '../../types/database'; + +// TODO: zi +/** + * Quickly change the application status of hacker. + * A hacker has multiple application statuses. + */ +export const handleModifyHacker = async () => { + // get hacker email + const hackerEmail = await input({ + message: 'Enter hacker email', + }); + + // query for hacker document + const hacker: UserData | null = await User.findOne({ email: hackerEmail }); + + // if hacker not found, re-prompt + if (!hacker) { + console.log('Oops, Hacker not found! Please try again.'); + return promptAction(); + } + + // get sub-action + const subAction = await select({ + message: 'Select sub-action to perform', + choices: [ + { + name: 'Change application status', + value: 'change-status', + }, + { + name: 'Delete application', + value: 'delete-application', + }, + { + name: 'Join a team', + value: 'join-team', + }, + { + name: 'Leave team', + value: 'leave-team', + }, + { + name: 'NFC check-in', + value: 'nfc-check-in', + }, + ], + }); + + // perform sub-action + switch (subAction) { + case 'change-status': + await changeStatus(hacker); + break; + case 'delete-application': + await deleteApplication(hacker); + break; + case 'join-team': + await joinTeam(hacker); + break; + case 'leave-team': + await leaveTeam(hacker); + break; + case 'nfc-check-in': + await nfcCheckIn(hacker); + break; + } +}; + +const changeStatus = async (hacker: UserData) => { + // get old status + const oldStatus: ApplicationStatus = hacker.applicationStatus; + const oldStatusString = getApplicationStatusString(oldStatus); + console.log(`Current status: ${oldStatus} (${oldStatusString})`); + + // query for new status + const newStatus: ApplicationStatus = await select({ + message: 'Select new status', + choices: [ + { + name: 'Change application status to CREATED', + value: ApplicationStatus.CREATED, + }, + { + name: 'Change application status to DECLINED', + value: ApplicationStatus.DECLINED, + }, + { + name: 'Change application status to STARTED', + value: ApplicationStatus.STARTED, + }, + { + name: 'Change application status to SUBMITTED', + value: ApplicationStatus.SUBMITTED, + }, + { + name: 'Change application status to ACCEPTED', + value: ApplicationStatus.ACCEPTED, + }, + { + name: 'Change application status to CONFIRMED', + value: ApplicationStatus.CONFIRMED, + }, + { + name: 'Change application status to REJECTED', + value: ApplicationStatus.REJECTED, + }, + { + name: 'Change application status to CHECKED_IN', + value: ApplicationStatus.CHECKED_IN, + }, + ], + }); + + // update hacker document and log + await User.updateOne({ email: hacker.email }, { applicationStatus: newStatus }); + console.log(`Changed application status from ${oldStatus} to ${newStatus}`); + + return promptAction(); +}; + +const deleteApplication = async (hacker: UserData) => { + // check if hacker has an application + if (!hacker.application) { + console.log('Hacker does not have an application'); + return promptAction(); + } + + // confirm deletion + const confirm = await select({ + message: `Application Found! Are you sure you want to delete it (${hacker.application})?`, + choices: [ + { + name: 'Yes', + value: true, + }, + { + name: 'No', + value: false, + }, + ], + }); + + // cancel if not confirmed + if (!confirm) { + console.log('Cancelled'); + return promptAction(); + } + + // perform deletion and log + await Application.deleteOne({ _id: hacker.application }); + await User.updateOne({ email: hacker.email }, { application: null }); + console.log('Deleted application successfully'); + + return promptAction(); +}; + +const joinTeam = async (hacker: UserData) => { + const teamId = hacker.team; + + // check if hacker has a team + if (teamId) { + console.log('Hacker already has a team'); + return promptAction(); + } + + // get team invite code + const inviteCode = await input({ + message: 'Enter team invite code', + }); + + // query for team document + const team = await Team.findOne({ joinCode: inviteCode }); + + // check if team exists + if (!team) { + console.log('Team does not exist'); + return promptAction(); + } + + // confirm joining team + const confirm = await select({ + message: `Team Found! Are you sure you want to join it (${team._id})?`, + choices: [ + { + name: 'Yes', + value: true, + }, + { + name: 'No', + value: false, + }, + ], + }); + + // cancel if not confirmed + if (!confirm) { + console.log('Cancelled'); + return promptAction(); + } + + // perform joining team and log + await Team.updateOne({ _id: team._id }, { $push: { members: hacker._id } }); + await User.updateOne({ email: hacker.email }, { team: team._id }); + console.log('Joined team successfully'); +}; + +const leaveTeam = async (hacker: UserData) => { + const teamId = hacker.team; + + // check if hacker has a team + if (!teamId) { + console.log('Hacker does not have a team'); + return promptAction(); + } + + const teamDoc: TeamData | null = await Team.findById(teamId); + + // check if team exists + if (!teamDoc) { + console.log('Team does not exist. Clearing hacker team field'); + await User.updateOne({ email: hacker.email }, { team: null }); + return promptAction(); + } + + // confirm deletion + const confirm = await select({ + message: `Team Found! Are you sure you want to leave it (${teamDoc._id})?`, + choices: [ + { + name: 'Yes', + value: true, + }, + { + name: 'No', + value: false, + }, + ], + }); + + // cancel if not confirmed + if (!confirm) { + console.log('Cancelled'); + return promptAction(); + } + + // perform deletion and log + await Team.updateOne({ _id: teamId }, { $pull: { members: hacker._id } }); + await User.updateOne({ email: hacker.email }, { team: null }); + console.log('Left team successfully'); +}; + +// TODO: this is not working +const nfcCheckIn = async (hacker: UserData) => { + // TODO: zi +}; + +const getApplicationStatusString = (status: ApplicationStatus): string => { + switch (status) { + case ApplicationStatus.CREATED: + return 'CREATED'; + case ApplicationStatus.DECLINED: + return 'DECLINED'; + case ApplicationStatus.STARTED: + return 'STARTED'; + case ApplicationStatus.SUBMITTED: + return 'SUBMITTED'; + case ApplicationStatus.ACCEPTED: + return 'ACCEPTED'; + case ApplicationStatus.CONFIRMED: + return 'CONFIRMED'; + case ApplicationStatus.REJECTED: + return 'REJECTED'; + case ApplicationStatus.CHECKED_IN: + return 'CHECKED_IN'; + } +}; diff --git a/scripts/dev-cli.ts b/scripts/dev-cli.ts new file mode 100644 index 00000000..5e064016 --- /dev/null +++ b/scripts/dev-cli.ts @@ -0,0 +1,139 @@ +import { select } from '@inquirer/prompts'; +import dbConnect from '../middleware/database'; +import * as dotenv from 'dotenv'; +import { handleModifyHacker } from './cli-util/modify-hacker'; +import { handleGetHacker } from './cli-util/get-hacker'; +import { handleGetTeam } from './cli-util/get-team'; +dotenv.config(); + +/** + * NOTE: To use this CLI tool, run `ts-node scripts/dev-cli.ts` in your terminal. + * + * executeCLI calls promptAction. + * + * promptAction calls one of the following functions: + * - handleGetHacker + * - handleGetTeam + * - handleModifyHacker + * - handleModifyTeam + * - handleClearCollection + * - handlePopulateCollection + * - process.exit(0) + */ +const executeCLI = async () => { + // Select database + const db = await select({ + message: 'Select database to perform action on', + choices: [ + { + name: 'Development Database', + value: 'dev-db', + }, + ], + }); + + // connect to db + await dbConnect(process.env.DATABASE_URL); + + // prompt action + await promptAction(); + + // exit + process.exit(0); +}; + +export const promptAction = async () => { + // TODO: + // list actions we as devs will need to do and can be simplified with this CLI tool + // [ ] populate collections with dummy data + // [ ] clear collection (danger) + // [G] get a document from a colleciton (i.e., hacker document using email) + // [ ] hacker document using email + // [ ] team document using team or hacker email or team invite code + // [Z] automatically allow a hacker to check in to an event (via nfc) + + // Select action + const action = await select({ + message: 'Select action to perform', + choices: [ + { + name: "Get a hacker's document", + value: 'get-hacker', + // sub-action: + // 1. see their events + // 2. see their team info + // 3. see their application + // 4. get entire JSON of their document + }, + { + name: 'Get a team document', + value: 'get-team', + // sub-action: + // 1. see their schedule + // 2. see their members + // 3. get entire JSON of their document + }, + { + name: "🏗️ Modify a hacker's document", + value: 'modify-hacker', + // sub-action: + // 1. change application status + // 2. delete application + // 3. join/leave team + // 4. check in + }, + { + name: '🏗️ Modify a team', + value: 'modify-team', + // sub-action: + // 1. change team name + // 2. change team members? + // 3. change team invite code? + // 4. change devpost link? + }, + { + name: '🏗️ Clear a collection (dangerous)', + value: 'clear-collection', + }, + { + name: '🏗️ Populate a collection', + value: 'populate-collection', + }, + { + name: 'Quit', + value: 'quit', + }, + ], + }); + + // Perform action + try { + switch (action) { + case 'get-hacker': + await handleGetHacker(); + break; + case 'get-team': + await handleGetTeam(); + break; + case 'modify-hacker': + await handleModifyHacker(); + break; + case 'quit': + process.exit(0); + break; + default: + console.log('Invalid action'); + } + } catch (err) { + handleError(err as Error); + } +}; + +// handle error +const handleError = (err: Error) => { + console.log('An error has occurred! You are a dev, right? Fix it: '); + console.log(err); +}; + +// Execute the CLI tool +executeCLI(); diff --git a/types/database.ts b/types/database.ts index 264549cf..72669c2a 100644 --- a/types/database.ts +++ b/types/database.ts @@ -46,6 +46,7 @@ export interface UserData { team?: mongoose.Schema.Types.ObjectId; application?: mongoose.Schema.Types.ObjectId; applicationStatus: ApplicationStatus; + eventsAttended: mongoose.Schema.Types.ObjectId[]; } export interface PreAddData { diff --git a/yarn.lock b/yarn.lock index 4b914d44..b3a814ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1201,6 +1201,124 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@inquirer/checkbox@^1.3.9": + version "1.3.9" + resolved "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-1.3.9.tgz" + integrity sha512-PQJ0XEijmoXxp7QBnUIiYvqx9YC4c/MyJY5UIDXzsyrSY56xuWyi95ggaKq4KMbcjXfxmXSwuqEYbzWu3hAFuQ== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + figures "^3.2.0" + +"@inquirer/confirm@^2.0.10": + version "2.0.10" + resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-2.0.10.tgz" + integrity sha512-P2B3LgCn26EfK9LeSGbi6WWNg/Q7ZTZYERZ2YRJtNaQC4dEXDWt5xDkgKEHXopBeaBXrlBpHQ7Lb3IdhvWnCfQ== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + chalk "^4.1.2" + +"@inquirer/core@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@inquirer/core/-/core-4.0.0.tgz" + integrity sha512-YBo2o6ijIatBU1l1ziByZeVF4YdzKQnYs8tBJ8SnysgmK3YYQz/L/w5w7QXs4OVnbzTWhXiC4mn9gQGbDihsPQ== + dependencies: + "@inquirer/type" "^1.1.2" + "@types/mute-stream" "^0.0.1" + "@types/node" "^20.5.6" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + cli-spinners "^2.9.0" + cli-width "^4.1.0" + figures "^3.2.0" + mute-stream "^1.0.0" + run-async "^3.0.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + +"@inquirer/editor@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@inquirer/editor/-/editor-1.2.8.tgz" + integrity sha512-DzvzhtLmkVEzfphCbEQz63IuTwmgpeSyyaKrHwCsKYr/eUaMLksQz33VrHbwYbsBq4oNm14OkikrVIMC/XhhPw== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + chalk "^4.1.2" + external-editor "^3.1.0" + +"@inquirer/expand@^1.1.9": + version "1.1.9" + resolved "https://registry.npmjs.org/@inquirer/expand/-/expand-1.1.9.tgz" + integrity sha512-8XuyeEVAEDCuDfK4+LVsOKfIOaC/Hen5nq+yMgyoQi4DgG77uLFtzjFBgOC0+HTEOugznF66DoWskUOmIN4x5Q== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + chalk "^4.1.2" + figures "^3.2.0" + +"@inquirer/input@^1.2.9": + version "1.2.9" + resolved "https://registry.npmjs.org/@inquirer/input/-/input-1.2.9.tgz" + integrity sha512-FC/wnXklGwUcGtlOU67T3pKHu6l1L5tIOHbZcqoLTlsdG8ukNARrX8h9Xrdgfr29t/LbvSxqdvYRmn4u2XRz0Q== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + chalk "^4.1.2" + +"@inquirer/password@^1.1.9": + version "1.1.9" + resolved "https://registry.npmjs.org/@inquirer/password/-/password-1.1.9.tgz" + integrity sha512-6L/SimCHutKVPDjkJBAkGO0POdJA3VXbdgAhCsX9katuyQSiMq5WGGa2Nqv7zXqiZxL5YuTPFqNNKKq00Q1HxA== + dependencies: + "@inquirer/input" "^1.2.9" + "@inquirer/type" "^1.1.2" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + +"@inquirer/prompts@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-3.0.4.tgz#8e6f071fe2d9a8b5fe5a2c573add90758c443618" + integrity sha512-xSBQjaj0zKGb+o6MiTd/Igl609JOwFf/DyU0vyiFiFR7u65n8JgCFVWLSeOWEFUP8L+AOGxZolhvuJ8KgCxIrg== + dependencies: + "@inquirer/checkbox" "^1.3.9" + "@inquirer/confirm" "^2.0.10" + "@inquirer/core" "^4.0.0" + "@inquirer/editor" "^1.2.8" + "@inquirer/expand" "^1.1.9" + "@inquirer/input" "^1.2.9" + "@inquirer/password" "^1.1.9" + "@inquirer/rawlist" "^1.2.9" + "@inquirer/select" "^1.2.9" + +"@inquirer/rawlist@^1.2.9": + version "1.2.9" + resolved "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-1.2.9.tgz" + integrity sha512-HqZeTP/F0l9LE0uCSbPHUbxVjoh8TLPzy8+BJzXyS9Na+XbbMOGN7KVNkNEGY+GH7X05jdOtd4gsO9DtAJUM2Q== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + chalk "^4.1.2" + +"@inquirer/select@^1.2.9": + version "1.2.9" + resolved "https://registry.npmjs.org/@inquirer/select/-/select-1.2.9.tgz" + integrity sha512-WXMQfUGzxrxdbDCU50xKqYCMoz+SOZnyV8sOeEJ8Ei5AjANz1fap3xA7EF8aZLJ9K1//m4OnyS/XDoi31Tqn+g== + dependencies: + "@inquirer/core" "^4.0.0" + "@inquirer/type" "^1.1.2" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + figures "^3.2.0" + +"@inquirer/type@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@inquirer/type/-/type-1.1.2.tgz" + integrity sha512-lowHTIBAE/bltg7/EchMDLfKUdT0BCyS2xzqgjsyKADybz2QZ0cLWtDXzjT1C6rdZI07Ng9jU4d2R9rDoU+6Hw== + "@next-auth/mongodb-adapter@^1.0.4": version "1.0.4" resolved "https://registry.npmjs.org/@next-auth/mongodb-adapter/-/mongodb-adapter-1.0.4.tgz" @@ -1355,10 +1473,17 @@ dependencies: mongodb "*" -"@types/node@*": - version "18.0.0" - resolved "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz" - integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== +"@types/mute-stream@^0.0.1": + version "0.0.1" + resolved "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.1.tgz" + integrity sha512-0yQLzYhCqGz7CQPE3iDmYjhb7KMBFOP+tBkyw+/Y2YyDI5wpS7itXXxneN1zSsUwWx3Ji6YiVYrhAnpQGS/vkw== + dependencies: + "@types/node" "*" + +"@types/node@*", "@types/node@^20.5.6": + version "20.5.9" + resolved "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz" + integrity sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ== "@types/prop-types@*": version "15.7.5" @@ -1399,6 +1524,11 @@ "@types/node" "*" "@types/webidl-conversions" "*" +"@types/wrap-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz" + integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== + "@typescript-eslint/parser@^5.21.0": version "5.30.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.0.tgz" @@ -1463,6 +1593,13 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-escapes@^4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -1475,7 +1612,7 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -1700,7 +1837,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1708,11 +1845,26 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== +cli-spinners@^2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== + +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" @@ -1901,6 +2053,11 @@ dotenv@^16.0.2: resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz" integrity sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" @@ -2196,6 +2353,15 @@ export-to-csv@^0.2.1: resolved "https://registry.npmjs.org/export-to-csv/-/export-to-csv-0.2.1.tgz" integrity sha512-KTbrd3CAZ0cFceJEZr1e5uiMasabeCpXq1/5uvVxDl53o4jXJHnltasQoj2NkzrxD8hU9kdwjnMhoir/7nNx/A== +external-editor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -2239,6 +2405,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -2458,6 +2631,13 @@ highlight-words-core@^1.2.0: resolved "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.2.tgz" integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== +iconv-lite@^0.4.24: + 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" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" @@ -2547,6 +2727,11 @@ is-extglob@^2.1.1: resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^3.0.0: + 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== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" @@ -2865,6 +3050,11 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mute-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + nanoid@^3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" @@ -3045,6 +3235,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" @@ -3717,6 +3912,11 @@ rrule@2.6.4: optionalDependencies: luxon "^1.21.3" +run-async@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz" + integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -3724,6 +3924,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + saslprep@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz" @@ -3828,6 +4033,15 @@ string-format@^2.0.0: resolved "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== +string-width@^4.1.0: + 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" + string.prototype.matchall@^4.0.7: version "4.0.7" resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz" @@ -3860,7 +4074,7 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" -strip-ansi@^6.0.1: +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -3921,6 +4135,13 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -3989,6 +4210,11 @@ type-fest@^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== +type-fest@^0.21.3: + 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== + typescript@4.8.3: version "4.8.3" resolved "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz" @@ -4075,6 +4301,15 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"