diff --git a/backend/scripts/add_reviews.ts b/backend/scripts/add_reviews.ts index c02d5f5d..18189155 100644 --- a/backend/scripts/add_reviews.ts +++ b/backend/scripts/add_reviews.ts @@ -19,6 +19,8 @@ const formatReview = (data: any): Review => ({ aptId: data.aptId.toString(), reviewText: data.reviewText, overallRating: data.overallRating, + bedrooms: data.bedrooms, + price: data.price, detailedRatings: { location: data['detailedRatings.location'], safety: data['detailedRatings.safety'], diff --git a/backend/scripts/add_reviews_nodups.ts b/backend/scripts/add_reviews_nodups.ts index 709c4c46..b44b3f2b 100644 --- a/backend/scripts/add_reviews_nodups.ts +++ b/backend/scripts/add_reviews_nodups.ts @@ -19,6 +19,8 @@ const formatReview = (data: any): Review => ({ aptId: data.aptId === null ? null : data.aptId.toString(), reviewText: data.reviewText, overallRating: data.overallRating, + bedrooms: data.bedrooms, + price: data.price, detailedRatings: { location: data['detailedRatings.location'], safety: data['detailedRatings.safety'], diff --git a/common/types/db-types.ts b/common/types/db-types.ts index 9407f012..914f6c67 100644 --- a/common/types/db-types.ts +++ b/common/types/db-types.ts @@ -19,13 +19,13 @@ export type Review = { readonly date: Date; readonly detailedRatings: DetailedRating; readonly landlordId: string; + readonly bedrooms: number; + readonly price: number; readonly overallRating: number; readonly photos: readonly string[]; readonly reviewText: string; readonly status?: 'PENDING' | 'APPROVED' | 'DECLINED' | 'DELETED'; readonly userId?: string | null; - readonly price?: number; - readonly bedrooms?: number; }; export type ReviewWithId = Review & Id; diff --git a/frontend/src/assets/bed-icon.svg b/frontend/src/assets/bed-icon.svg new file mode 100644 index 00000000..ca6f0e68 --- /dev/null +++ b/frontend/src/assets/bed-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/money-icon.svg b/frontend/src/assets/money-icon.svg new file mode 100644 index 00000000..668674ef --- /dev/null +++ b/frontend/src/assets/money-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/xIcon.svg b/frontend/src/assets/xIcon.svg new file mode 100644 index 00000000..8b388fc7 --- /dev/null +++ b/frontend/src/assets/xIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/Admin/AdminReview.tsx b/frontend/src/components/Admin/AdminReview.tsx index b6983339..2dfe0c06 100644 --- a/frontend/src/components/Admin/AdminReview.tsx +++ b/frontend/src/components/Admin/AdminReview.tsx @@ -24,6 +24,7 @@ import { get } from '../../utils/call'; import { Link as RouterLink } from 'react-router-dom'; import ReviewHeader from '../Review/ReviewHeader'; import axios from 'axios'; +import getPriceRange from '../../utils/priceRange'; import { createAuthHeaders, getUser } from '../../utils/firebase'; /** @@ -55,6 +56,12 @@ const useStyles = makeStyles(() => ({ dateText: { color: colors.gray1, }, + bedroomsPriceText: { + marginTop: '10px', + marginBottom: '10px', + display: 'flex', + gap: '30px', + }, ratingInfo: { marginTop: '10px', marginBottom: '30px', @@ -81,9 +88,9 @@ const useStyles = makeStyles(() => ({ * @returns The rendered component. */ const AdminReviewComponent = ({ review, setToggle, declinedSection }: Props): ReactElement => { - const { detailedRatings, overallRating, date, reviewText, photos } = review; + const { detailedRatings, overallRating, bedrooms, price, date, reviewText, photos } = review; const formattedDate = format(new Date(date), 'MMM dd, yyyy').toUpperCase(); - const { root, dateText, ratingInfo, photoStyle, photoRowStyle } = useStyles(); + const { root, dateText, bedroomsPriceText, ratingInfo, photoStyle, photoRowStyle } = useStyles(); const [apt, setApt] = useState([]); const [landlord, setLandlord] = useState(); @@ -172,6 +179,18 @@ const AdminReviewComponent = ({ review, setToggle, declinedSection }: Props): Re {formattedDate} + + {bedrooms > 0 && ( + Bedroom(s): {bedrooms} + )} + {price > 0 && ( + + {' '} + Price: {getPriceRange(price) || 0} + + )} + + diff --git a/frontend/src/components/LeaveReview/ReviewModal.tsx b/frontend/src/components/LeaveReview/ReviewModal.tsx index f5b428e0..8de8ced9 100644 --- a/frontend/src/components/LeaveReview/ReviewModal.tsx +++ b/frontend/src/components/LeaveReview/ReviewModal.tsx @@ -9,16 +9,25 @@ import { Input, FormLabel, Typography, + makeStyles, + IconButton, + CardMedia, + Icon, + useMediaQuery, } from '@material-ui/core'; import axios from 'axios'; -import React, { Dispatch, SetStateAction, useReducer, useState } from 'react'; +import React, { Dispatch, SetStateAction, useReducer, useState, useRef, useEffect } from 'react'; import { DetailedRating, Review } from '../../../../common/types/db-types'; -import { splitArr } from '../../utils'; import { createAuthHeaders, uploadFile } from '../../utils/firebase'; import ReviewRating from './ReviewRating'; import { includesProfanity } from '../../utils/profanity'; import Toast from './Toast'; import styles from './ReviewModal.module.scss'; +import DropDown from '../utils/DropDown'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { ReactComponent as XIcon } from '../../assets/xIcon.svg'; +import { colors } from '../../colors'; +import getPriceRange from '../../utils/priceRange'; const REVIEW_CHARACTER_LIMIT = 2000; const REVIEW_PHOTOS_LIMIT = 3; @@ -36,7 +45,133 @@ interface Props { user: firebase.User | null; } +const useStyle = makeStyles({ + modalWidth: { + maxWidth: '750px', + }, + leaveAReviewTitle: { + fontSize: '23px', + fontWeight: 600, + marginTop: '15px', + }, + dropDownButton: { + backgroundColor: '#E8E8E8', + border: 'none', + borderRadius: '50px', + paddingRight: '5px', + paddingLeft: '5px', + position: 'relative', + minHeight: '40px', + display: 'flex', + alignItems: 'center', + }, + dropDownStyle: { + fontWeight: 600, + width: '100%', + height: '100%', + borderRadius: '50px !important', + backgroundColor: 'transparent', + top: '0', + left: '0', + textTransform: 'none', + fontSize: '18px', + textAlign: 'left', + position: 'absolute', + zIndex: 1, + display: 'flex', + justifyContent: 'left', + paddingLeft: '25px', + }, + expandMoreIcon: { + right: '10px', + position: 'absolute', + }, + promptBox: { + borderRadius: '8px', + boxShadow: '0px 0px 4px 0px rgba(0, 0, 0, 0.25)', + padding: '16px', + }, + promptTitle: { + fontSize: '17px', + fontWeight: 600, + }, + promptList: { + marginBottom: '8px', + }, + submitButton: { + borderRadius: '30px', + marginTop: '10px', + marginBottom: '10px', + width: '80px', + marginRight: '15px', + }, + hollowRedButton: { + minWidth: '80px', + height: '35px', + borderRadius: '30px', + border: '2px solid', + borderColor: `${colors.red1} !important`, + backgroundColor: 'transparent', + color: colors.red1, + '&:hover': { + backgroundColor: 'rgba(0,0,0,0.1)', + opacity: 0.8, + }, + }, + disabledButton: { + minWidth: '80px', + height: '35px', + borderRadius: '30px', + border: '2px solid', + borderColor: '#ced4da', + backgroundColor: 'transparent', + color: '#ced4da', + pointerEvents: 'none', + cursor: 'default', + }, + photoHover: { + backgroundColor: 'black', + position: 'absolute', + borderRadius: '6px', + height: '100%', + width: '100%', + }, + photosContainer: { + display: 'flex', + gap: '12px', + alignItems: 'center', + }, + photoRemoveButton: { + fill: 'white', + cursor: 'pointer', + display: 'none', + position: 'absolute', + zIndex: 1, + }, + photoAndButton: { + position: 'relative', + borderRadius: '6px', + '&:hover $photoRemoveButton': { + display: 'block', + }, + '&:hover $photo': { + opacity: 0.8, + }, + }, + photo: { + position: 'absolute', + height: '100%', + width: '100%', + borderRadius: '6px', + '&:hover': { + opacity: 0.8, + }, + }, +}); + interface FormData { + bedrooms: number; + price: number; overallRating: number; address: string; ratings: DetailedRating; @@ -45,6 +180,8 @@ interface FormData { } const defaultReview: FormData = { + bedrooms: 0, + price: 0, overallRating: 0, address: '', ratings: { @@ -60,6 +197,8 @@ const defaultReview: FormData = { }; type Action = + | { type: 'updateBedrooms'; bedrooms: number } + | { type: 'updatePrice'; price: number } | { type: 'updateOverall'; rating: number } | { type: 'updateAddress'; address: string } | { type: 'updateRating'; category: keyof DetailedRating; rating: number } @@ -69,6 +208,10 @@ type Action = const reducer = (state: FormData, action: Action): FormData => { switch (action.type) { + case 'updateBedrooms': + return { ...state, bedrooms: action.bedrooms }; + case 'updatePrice': + return { ...state, price: action.price }; case 'updateOverall': return { ...state, overallRating: action.rating }; case 'updateAddress': @@ -86,6 +229,30 @@ const reducer = (state: FormData, action: Action): FormData => { } }; +/** + * ReviewModal Component + * + * This component displays a modal for users to input information for their review about a specific apartment. + * This includes the bedroom(s), price per person, overall rating, detailed ratings (location, safety, maintenance, + * conditions), review text/body, pictures (up to 3 pictures). The information that is required are: overall experience + * and review text/body, all other information are optional. + * The submit button will add the review to the database and set the status as PENDING until an admin approves it. + * The modal is responsive for all screen sizes and mobile display. + * + * @component + * @param {Object} props - Component properties. + * @param {boolean} props.open – The boolean for whether the modal is open or not. + * @param props.onClose – The function to be called when the modal is closed. + * @param props.setOpen – The function to set the modal open. + * @param {string} props.landlordId – The landlord ID of the apartment that is being reviewed. + * @param props.onSuccess – The function to be called when the review has been successfully submitted. + * @param {number} props.toastTime – The time in milliseconds which the review successfully submitted toast is shown. + * @param {string} props.aptId – The Apartment ID of the apartment being reviewed. + * @param {string} props.aptName – The name of the apartment being reviewed. + * @param props.user – The current user, null if not logged in. + * @returns + */ + const ReviewModal = ({ open, onClose, @@ -102,6 +269,36 @@ const ReviewModal = ({ const [emptyTextError, setEmptyTextError] = useState(false); const [ratingError, setRatingError] = useState(false); const [includesProfanityError, setIncludesProfanityError] = useState(false); + const [addedPhoto, setAddedPhoto] = useState(false); + const modalRef = useRef(null); + const isMobile = useMediaQuery('(max-width:600px)'); + + const { + modalWidth, + leaveAReviewTitle, + dropDownButton, + dropDownStyle, + expandMoreIcon, + promptBox, + promptTitle, + promptList, + submitButton, + hollowRedButton, + disabledButton, + photo, + photoHover, + photosContainer, + photoRemoveButton, + photoAndButton, + } = useStyle(); + + const updateBedrooms = (bedrooms: number) => { + dispatch({ type: 'updateBedrooms', bedrooms }); + }; + + const updatePrice = (price: number) => { + dispatch({ type: 'updatePrice', price }); + }; const updateOverall = () => { return (_: React.ChangeEvent<{}>, value: number | null) => { @@ -120,17 +317,22 @@ const ReviewModal = ({ const updatePhotos = (event: React.ChangeEvent) => { const { files } = event.target; - if (!files || files.length > REVIEW_PHOTOS_LIMIT) { + if (!files) return; + const availablePhotos = REVIEW_PHOTOS_LIMIT - review.localPhotos.length; + if (availablePhotos <= 0) { console.log(`Max file limit of ${REVIEW_PHOTOS_LIMIT} exceeded`); return; } - const photos = [...files]; - const bigPhoto = photos.find((photo) => photo.size > REVIEW_PHOTO_MAX_MB * Math.pow(1024, 2)); + + const newFiles = [...files].slice(0, availablePhotos); + const bigPhoto = newFiles.find( + (newFiles) => newFiles.size > REVIEW_PHOTO_MAX_MB * Math.pow(1024, 2) + ); if (bigPhoto) { console.log(`File ${bigPhoto.name} exceeds max size of ${REVIEW_PHOTO_MAX_MB}`); return; } - dispatch({ type: 'updatePhotos', photos }); + dispatch({ type: 'updatePhotos', photos: [...review.localPhotos, ...newFiles] }); }; const updateBody = (event: React.ChangeEvent) => { @@ -138,6 +340,8 @@ const ReviewModal = ({ }; const formDataToReview = async ({ + bedrooms, + price, overallRating, ratings, body, @@ -149,6 +353,8 @@ const ReviewModal = ({ date: new Date(), detailedRatings: ratings, landlordId: landlordId, + bedrooms: bedrooms, + price: price, overallRating, photos, reviewText: body, @@ -172,6 +378,9 @@ const ReviewModal = ({ includesProfanity(data.reviewText) ? setIncludesProfanityError(true) : setIncludesProfanityError(false); + if (modalRef.current) { + modalRef.current.scrollTop = 0; + } return; } const res = await axios.post('/api/new-review', data, createAuthHeaders(token)); @@ -193,17 +402,61 @@ const ReviewModal = ({ } }; - const generateFileStatus = (): string => { - if (!review.localPhotos) return 'No photos uploaded'; - const [first, rest] = splitArr([...review.localPhotos], 2); - const fileNames = first.map((file) => file.name).join(', '); - return fileNames + (rest.length ? `, and ${rest.length} more` : ''); + const onCloseClearPhotos = () => { + dispatch({ type: 'updatePhotos', photos: [] }); + onClose(); + }; + + const removePhoto = (index: number) => { + const newPhotos = review.localPhotos.filter((_, photoIndex) => index !== photoIndex); + dispatch({ type: 'updatePhotos', photos: newPhotos }); + }; + + useEffect(() => { + const updateScrollPosition = () => { + if (modalRef.current) { + const { scrollHeight, clientHeight } = modalRef.current; + const maxScrollTop = scrollHeight - clientHeight; + modalRef.current.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0; + } + }; + const timer = setTimeout(updateScrollPosition, 100); + return () => clearTimeout(timer); + }, [addedPhoto]); + + /** + * Returns the "Things to consider in your review:" prompt box. Function serves to help mobile display. + */ + const reviewPromptBox = () => { + return ( +
+ Things to consider in your review: +
    +
  • What do you like/dislike about your apartment?
  • +
  • Is there a lot of noise?
  • +
  • What amenities are included?
  • +
  • Is laundry convenient?
  • +
  • How responsive is your landlord?
  • +
  • Are there maintenance or other additional fees?
  • +
+
+ ); }; return ( - - Leave a Review{aptName.length > 0 && `: ${aptName}`} - + + + Leave a Review{aptName.length > 0 && `: ${aptName}`} + + {/* This div padding prevents the scrollbar from displaying unnecessarily */}
@@ -215,6 +468,81 @@ const ReviewModal = ({ time={toastTime} /> )} + + + + Bedroom(s) + + + ({ + item: `${i + 1} Bedroom${i > 0 ? 's' : ''}`, + callback: () => updateBedrooms(i + 1), + }))} + defaultValue="Select" + className={dropDownStyle} + icon={false} + /> + + + + + + + Price Per Person + + + ({ + item: getPriceRange(i + 1), + callback: () => updatePrice(i + 1), + }))} + defaultValue="Select" + className={dropDownStyle} + icon={false} + /> + + + + - - +
- - - Upload Pictures: + + + {isMobile && reviewPromptBox()} + + - - - {generateFileStatus()} - - - + + {!isMobile && ( + + {reviewPromptBox()} + )} + + + + +
+ Upload Pictures: + + {`Reviewers may upload up to ${REVIEW_PHOTOS_LIMIT} photos. Max photo size of ${REVIEW_PHOTO_MAX_MB}MB`} + +
- - - {`Reviewers may upload up to ${REVIEW_PHOTOS_LIMIT} photos. Max photo size of ${REVIEW_PHOTO_MAX_MB}MB`} - + + -
- - + + {review.localPhotos.length > 0 && ( + + {review.localPhotos.map((p, index) => { + return ( +
+
+ + removePhoto(index)} + style={ + !isMobile + ? { position: 'absolute', right: '2px' } + : { position: 'absolute', right: '10px', top: '10px' } + } + > + + +
+ ); + })} + + )}
- - + + diff --git a/frontend/src/components/LeaveReview/ReviewRating.tsx b/frontend/src/components/LeaveReview/ReviewRating.tsx index a2a00bb7..1699587f 100644 --- a/frontend/src/components/LeaveReview/ReviewRating.tsx +++ b/frontend/src/components/LeaveReview/ReviewRating.tsx @@ -11,10 +11,10 @@ interface Props { const ReviewRating = ({ name, label, onChange }: Props) => { return ( - + - + {label} diff --git a/frontend/src/components/Review/Review.tsx b/frontend/src/components/Review/Review.tsx index 93926650..ad279d81 100644 --- a/frontend/src/components/Review/Review.tsx +++ b/frontend/src/components/Review/Review.tsx @@ -11,6 +11,7 @@ import { IconButton, Collapse, Link, + useMediaQuery, } from '@material-ui/core'; import HeartRating from '../utils/HeartRating'; import { format } from 'date-fns'; @@ -30,6 +31,9 @@ import ReviewHeader from './ReviewHeader'; import { Link as RouterLink } from 'react-router-dom'; import { createAuthHeaders, getUser } from '../../utils/firebase'; import { get } from '../../utils/call'; +import getPriceRange from '../../utils/priceRange'; +import { ReactComponent as BedIcon } from '../../assets/bed-icon.svg'; +import { ReactComponent as MoneyIcon } from '../../assets/money-icon.svg'; type Props = { readonly review: ReviewWithId; @@ -69,7 +73,9 @@ const useStyles = makeStyles(() => ({ dateText: { color: colors.gray1, }, - heartSpacing: { + reviewHeader: { + display: 'flex', + alignItems: 'center', marginBottom: '10px', }, button: { @@ -91,6 +97,18 @@ const useStyles = makeStyles(() => ({ paddingTop: '2%', paddingLeft: '0.6%', }, + bedroomsPrice: { + display: 'flex', + alignItems: 'center', + gap: '10px', + }, + bedroomsPriceText: { + fontWeight: 600, + }, + bedPriceIcon: { + width: '21px', + height: '21px', + }, })); const ReviewComponent = ({ @@ -104,7 +122,8 @@ const ReviewComponent = ({ setUser, showLabel, }: Props): ReactElement => { - const { id, detailedRatings, overallRating, date, reviewText, likes, photos } = review; + const { id, detailedRatings, overallRating, date, bedrooms, price, reviewText, likes, photos } = + review; const formattedDate = format(new Date(date), 'MMM dd, yyyy').toUpperCase(); const { root, @@ -115,13 +134,17 @@ const ReviewComponent = ({ photoStyle, photoRowStyle, bottomborder, - heartSpacing, + reviewHeader, apartmentIndicator, + bedroomsPrice, + bedPriceIcon, + bedroomsPriceText, } = useStyles(); const [expanded, setExpanded] = useState(false); const [expandedText, setExpandedText] = useState(false); const [apt, setApt] = useState([]); const [landlordData, setLandlordData] = useState(); + const isMobile = useMediaQuery('(max-width:600px)'); const handleExpandClick = () => { setExpanded(!expanded); @@ -212,6 +235,33 @@ const ReviewComponent = ({ ); }; + const bedroomsPriceLabel = () => { + return ( + + {bedrooms > 0 && ( +
+ + + {bedrooms} {bedrooms == 1 ? 'Bedroom' : 'Bedrooms'} + +
+ )} + {price > 0 && ( +
+ + {getPriceRange(price) || 0} +
+ )} +
+ ); + }; + return ( @@ -219,8 +269,8 @@ const ReviewComponent = ({ - - + + @@ -237,11 +287,13 @@ const ReviewComponent = ({ + {!isMobile && bedroomsPriceLabel()} + {formattedDate} - + {isMobile && bedroomsPriceLabel()} diff --git a/frontend/src/components/utils/DropDown.tsx b/frontend/src/components/utils/DropDown.tsx index bcfab486..1ab8b12b 100644 --- a/frontend/src/components/utils/DropDown.tsx +++ b/frontend/src/components/utils/DropDown.tsx @@ -11,6 +11,9 @@ type MenuElement = { type Props = { menuItems: MenuElement[]; + defaultValue?: string; + className?: string; + icon?: boolean; }; const useStyles = makeStyles({ @@ -21,9 +24,9 @@ const useStyles = makeStyles({ }, }); -export default function BasicMenu({ menuItems }: Props) { +export default function BasicMenu({ menuItems, defaultValue, className, icon }: Props) { const [anchorEl, setAnchorEl] = useState(null); - const [selected, setSelected] = useState(menuItems[0].item || '-'); + const [selected, setSelected] = useState(defaultValue ? defaultValue : 'Recent'); const { button } = useStyles(); const [isMobile, setIsMobile] = useState(false); @@ -49,10 +52,10 @@ export default function BasicMenu({ menuItems }: Props) { aria-haspopup="true" aria-expanded={open ? 'true' : undefined} onClick={handleClick} - className={button} + className={className || button} > {selected} - + {icon != false && } ; const StyledRating = withStyles({ iconEmpty: { - color: colors.red1, + color: colors.red2, }, iconFilled: { - color: colors.red1, + color: colors.red2, }, })(Rating); diff --git a/frontend/src/pages/LandlordPage.tsx b/frontend/src/pages/LandlordPage.tsx index 753cdd76..1aaa009e 100644 --- a/frontend/src/pages/LandlordPage.tsx +++ b/frontend/src/pages/LandlordPage.tsx @@ -287,16 +287,6 @@ const LandlordPage = ({ user, setUser }: Props): ReactElement => { } }; - const openReviewModal = async () => { - let user = await getUser(true); - setUser(user); - if (!user) { - showSignInErrorToast(); - return; - } - setReviewOpen(true); - }; - // Define a component 'Modals' conditionally based on landlordData existence const Modals = landlordData && ( <> @@ -375,7 +365,7 @@ const LandlordPage = ({ user, setUser }: Props): ReactElement => { style={{ width: '107px', height: '43px' }} /> - + */} @@ -478,15 +468,6 @@ const LandlordPage = ({ user, setUser }: Props): ReactElement => { style={{ width: '107px', height: '43px' }} /> - diff --git a/frontend/src/utils/priceRange.ts b/frontend/src/utils/priceRange.ts new file mode 100644 index 00000000..8c710f27 --- /dev/null +++ b/frontend/src/utils/priceRange.ts @@ -0,0 +1,26 @@ +const priceOptions = [ + '< $800', + '$800 - $1K', + '$1 - $1.2K', + '$1.2 - $1.4K', + '$1.4 - $1.6K', + '$1.6K+', +]; + +/** + * Returns the price range option given the (1-based) index. + * The price range options are as following: + * 1: < $800 + * 2: $800 - $1K + * 3: $1 - $1.2K + * 4: $1.2 - $1.4K + * 5: $1.4 - $1.6K + * 6: $1.6K+ + * If an out-of-range index is given, the function will return undefined. + * + * @param {number} priceNum – The 1-based index of the desired price range. + * @returns {string} The string of the corresponding price range. + */ +export default function getPriceRange(priceNum: number): string { + return priceOptions[priceNum - 1]; +} diff --git a/package.json b/package.json index 5bde73d4..fbea843e 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "dependencies": { "bad-words": "^3.0.4", "env-cmd": "^10.1.0", - "nodemailer": "^6.9.12", + "nodemailer": "^6.9.13", "react-google-charts": "^4.0.1", "react-infinite-scroll-component": "^6.1.0" } diff --git a/yarn.lock b/yarn.lock index 4fd364a9..3adcc729 100644 --- a/yarn.lock +++ b/yarn.lock @@ -297,9 +297,9 @@ "js-tokens" "^4.0.0" "@babel/parser@^7.1.0", "@babel/parser@^7.12.3", "@babel/parser@^7.14.7", "@babel/parser@^7.16.0", "@babel/parser@^7.16.5", "@babel/parser@^7.7.0": - "integrity" "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==" - "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz" - "version" "7.16.6" + "integrity" "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==" + "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz" + "version" "7.23.9" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.2": "integrity" "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==" @@ -3417,10 +3417,10 @@ dependencies: "retry" "0.13.1" -"async@^2.6.2": - "integrity" "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==" - "resolved" "https://registry.npmjs.org/async/-/async-2.6.3.tgz" - "version" "2.6.3" +"async@^2.6.4": + "integrity" "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==" + "resolved" "https://registry.npmjs.org/async/-/async-2.6.4.tgz" + "version" "2.6.4" dependencies: "lodash" "^4.17.14" @@ -3675,7 +3675,7 @@ "resolved" "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz" "version" "6.18.0" -"backend@file:/Users/kearoy/Documents/03 Cornell Files/DTI/cu-apts/backend": +"backend@file:/Users/grace-sawatyanon/cu-apts/backend": "resolved" "file:backend" "version" "1.0.0" dependencies: @@ -4160,9 +4160,7 @@ "lodash.uniq" "^4.5.0" "caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30000981", "caniuse-lite@^1.0.30001109", "caniuse-lite@^1.0.30001125", "caniuse-lite@^1.0.30001286": - "integrity" "sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==" - "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001564.tgz" - "version" "1.0.30001564" + "version" "1.0.30001431" "capture-exit@^2.0.0": "integrity" "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==" @@ -4476,9 +4474,9 @@ "color-string" "^1.6.0" "colorette@^2.0.16": - "integrity" "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" - "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz" - "version" "2.0.16" + "integrity" "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + "version" "2.0.20" "combined-stream@^1.0.6", "combined-stream@^1.0.8", "combined-stream@~1.0.6": "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==" @@ -5146,13 +5144,6 @@ dependencies: "ms" "2.0.0" -"debug@^3.1.1": - "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" - "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - "version" "3.2.7" - dependencies: - "ms" "^2.1.1" - "debug@^3.2.5": "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -5168,9 +5159,9 @@ "ms" "^2.1.1" "debug@^4.0.1", "debug@^4.1.0", "debug@^4.1.1", "debug@^4.2.0", "debug@^4.3.1", "debug@^4.3.2", "debug@4": - "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" - "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" - "version" "4.3.3" + "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + "version" "4.3.4" dependencies: "ms" "2.1.2" @@ -5213,7 +5204,7 @@ "object-keys" "^1.1.1" "regexp.prototype.flags" "^1.2.0" -"deep-is@^0.1.3", "deep-is@~0.1.3": +"deep-is@^0.1.3": "integrity" "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" "version" "0.1.4" @@ -5768,14 +5759,13 @@ "version" "4.0.0" "escodegen@^2.0.0": - "integrity" "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==" - "resolved" "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" - "version" "2.0.0" + "integrity" "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==" + "resolved" "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz" + "version" "2.1.0" dependencies: "esprima" "^4.0.1" "estraverse" "^5.2.0" "esutils" "^2.0.2" - "optionator" "^0.8.1" optionalDependencies: "source-map" "~0.6.1" @@ -6301,7 +6291,7 @@ "resolved" "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" "version" "2.1.0" -"fast-levenshtein@^2.0.6", "fast-levenshtein@~2.0.6": +"fast-levenshtein@^2.0.6": "integrity" "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" "resolved" "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" "version" "2.0.6" @@ -6622,7 +6612,7 @@ "inherits" "^2.0.1" "readable-stream" "^2.0.0" -"frontend@file:/Users/kearoy/Documents/03 Cornell Files/DTI/cu-apts/frontend": +"frontend@file:/Users/grace-sawatyanon/cu-apts/frontend": "resolved" "file:frontend" "version" "0.1.0" dependencies: @@ -6852,14 +6842,14 @@ "is-glob" "^4.0.1" "glob@^7.0.3", "glob@^7.1.1", "glob@^7.1.2", "glob@^7.1.3", "glob@^7.1.4", "glob@^7.1.6", "glob@^7.1.7": - "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" - "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" - "version" "7.2.0" + "integrity" "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + "version" "7.2.3" dependencies: "fs.realpath" "^1.0.0" "inflight" "^1.0.4" "inherits" "2" - "minimatch" "^3.0.4" + "minimatch" "^3.1.1" "once" "^1.3.0" "path-is-absolute" "^1.0.0" @@ -6975,9 +6965,9 @@ "node-forge" "^0.10.0" "graceful-fs@^4.1.11", "graceful-fs@^4.1.15", "graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0", "graceful-fs@^4.2.4": - "integrity" "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" - "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" - "version" "4.2.8" + "integrity" "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + "version" "4.2.10" "growly@^1.3.0": "integrity" "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" @@ -7519,9 +7509,9 @@ "version" "2.1.0" "ip@^1.1.0", "ip@^1.1.5": - "integrity" "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - "resolved" "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" - "version" "1.1.5" + "integrity" "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + "resolved" "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz" + "version" "1.1.8" "ipaddr.js@^1.9.0", "ipaddr.js@1.9.1": "integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" @@ -8846,14 +8836,6 @@ "prelude-ls" "^1.2.1" "type-check" "~0.4.0" -"levn@~0.3.0": - "integrity" "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=" - "resolved" "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" - "version" "0.3.0" - dependencies: - "prelude-ls" "~1.1.2" - "type-check" "~0.3.2" - "limiter@^1.1.5": "integrity" "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" "resolved" "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz" @@ -9323,17 +9305,17 @@ "bn.js" "^4.0.0" "brorand" "^1.0.1" -"mime-db@>= 1.43.0 < 2", "mime-db@1.51.0": - "integrity" "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz" - "version" "1.51.0" +"mime-db@>= 1.43.0 < 2", "mime-db@1.52.0": + "integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + "version" "1.52.0" "mime-types@^2.0.8", "mime-types@^2.1.12", "mime-types@^2.1.27", "mime-types@~2.1.17", "mime-types@~2.1.19", "mime-types@~2.1.24": - "integrity" "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==" - "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz" - "version" "2.1.34" + "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + "version" "2.1.35" dependencies: - "mime-db" "1.51.0" + "mime-db" "1.52.0" "mime@^2.4.4", "mime@^2.4.6": "integrity" "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" @@ -9395,10 +9377,17 @@ dependencies: "brace-expansion" "^1.1.7" -"minimist@^1.1.1", "minimist@^1.2.0", "minimist@^1.2.5": - "integrity" "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" - "version" "1.2.5" +"minimatch@^3.1.1": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "brace-expansion" "^1.1.7" + +"minimist@^1.1.1", "minimist@^1.2.0", "minimist@^1.2.5", "minimist@^1.2.6": + "integrity" "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + "version" "1.2.8" "minipass-collect@^1.0.2": "integrity" "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==" @@ -9460,12 +9449,12 @@ "for-in" "^1.0.2" "is-extendable" "^1.0.1" -"mkdirp@^0.5.1", "mkdirp@^0.5.3", "mkdirp@^0.5.5", "mkdirp@~0.5.1": - "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" - "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - "version" "0.5.5" +"mkdirp@^0.5.1", "mkdirp@^0.5.3", "mkdirp@^0.5.6", "mkdirp@~0.5.1": + "integrity" "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==" + "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + "version" "0.5.6" dependencies: - "minimist" "^1.2.5" + "minimist" "^1.2.6" "mkdirp@^1.0.3": "integrity" "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" @@ -9534,9 +9523,9 @@ "thunky" "^1.0.2" "nan@^2.12.1": - "integrity" "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - "resolved" "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz" - "version" "2.15.0" + "integrity" "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" + "resolved" "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz" + "version" "2.18.0" "nanoid@^3.1.30": "integrity" "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==" @@ -9686,10 +9675,10 @@ "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz" "version" "2.0.1" -"nodemailer@^6.9.12": - "integrity" "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==" - "resolved" "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz" - "version" "6.9.12" +"nodemailer@^6.9.13": + "integrity" "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==" + "resolved" "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz" + "version" "6.9.13" "normalize-package-data@^2.3.2", "normalize-package-data@^2.5.0": "integrity" "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==" @@ -9950,18 +9939,6 @@ "cssnano" "^4.1.10" "last-call-webpack-plugin" "^3.0.0" -"optionator@^0.8.1": - "integrity" "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==" - "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" - "version" "0.8.3" - dependencies: - "deep-is" "~0.1.3" - "fast-levenshtein" "~2.0.6" - "levn" "~0.3.0" - "prelude-ls" "~1.1.2" - "type-check" "~0.3.2" - "word-wrap" "~1.2.3" - "optionator@^0.9.1": "integrity" "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==" "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" @@ -10352,13 +10329,13 @@ "version" "1.16.1-lts" "portfinder@^1.0.26": - "integrity" "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==" - "resolved" "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz" - "version" "1.0.28" + "integrity" "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==" + "resolved" "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz" + "version" "1.0.32" dependencies: - "async" "^2.6.2" - "debug" "^3.1.1" - "mkdirp" "^0.5.5" + "async" "^2.6.4" + "debug" "^3.2.7" + "mkdirp" "^0.5.6" "posix-character-classes@^0.1.0": "integrity" "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" @@ -11049,11 +11026,6 @@ "resolved" "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" "version" "1.2.1" -"prelude-ls@~1.1.2": - "integrity" "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - "resolved" "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" - "version" "1.1.2" - "prepend-http@^1.0.0": "integrity" "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" "resolved" "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" @@ -12069,11 +12041,11 @@ "aproba" "^1.1.1" "rxjs@^7.4.0": - "integrity" "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==" - "resolved" "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz" - "version" "7.4.0" + "integrity" "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==" + "resolved" "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz" + "version" "7.8.1" dependencies: - "tslib" "~2.1.0" + "tslib" "^2.1.0" "safe-buffer@^5.0.1", "safe-buffer@^5.1.0", "safe-buffer@^5.1.1", "safe-buffer@^5.1.2", "safe-buffer@^5.2.0", "safe-buffer@>=5.1.0", "safe-buffer@~5.2.0", "safe-buffer@5.2.1": "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" @@ -13054,9 +13026,9 @@ "has-flag" "^4.0.0" "supports-hyperlinks@^2.0.0": - "integrity" "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==" - "resolved" "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz" - "version" "2.2.0" + "integrity" "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==" + "resolved" "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" + "version" "2.3.0" dependencies: "has-flag" "^4.0.0" "supports-color" "^7.0.0" @@ -13407,11 +13379,6 @@ "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" "version" "2.3.1" -"tslib@~2.1.0": - "integrity" "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz" - "version" "2.1.0" - "tsutils@^3.17.1", "tsutils@^3.21.0": "integrity" "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==" "resolved" "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -13443,13 +13410,6 @@ dependencies: "prelude-ls" "^1.2.1" -"type-check@~0.3.2": - "integrity" "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=" - "resolved" "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" - "version" "0.3.2" - dependencies: - "prelude-ls" "~1.1.2" - "type-check@~0.4.0": "integrity" "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==" "resolved" "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" @@ -14064,7 +14024,7 @@ dependencies: "isexe" "^2.0.0" -"word-wrap@^1.2.3", "word-wrap@~1.2.3": +"word-wrap@^1.2.3": "integrity" "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" "resolved" "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" "version" "1.2.3"