├── server ├── .gitignore ├── routes │ ├── message.js │ ├── chat.js │ └── user.js ├── mongoDB │ └── connection.js ├── models │ ├── messageModel.js │ ├── chatModel.js │ └── userModel.js ├── package.json ├── middleware │ └── user.js ├── controllers │ ├── messageControllers.js │ ├── chatControllers.js │ └── user.js ├── index.js └── package-lock.json ├── .babelrc ├── .prettierrc ├── clients ├── src │ ├── index.css │ ├── assets │ │ ├── logo.png │ │ └── no-contacts.jpg │ ├── setupTests.js │ ├── App.test.js │ ├── components │ │ ├── ui │ │ │ ├── SkeletonLoading.jsx │ │ │ ├── Typing.jsx │ │ │ ├── Loading.jsx │ │ │ └── NoContacts.jsx │ │ ├── Start.jsx │ │ ├── group │ │ │ └── Search.jsx │ │ ├── profile │ │ │ └── InputEdit.jsx │ │ ├── MessageHistory.jsx │ │ ├── Contacts.jsx │ │ ├── Profile.jsx │ │ ├── Group.jsx │ │ └── Model.jsx │ ├── reportWebVitals.js │ ├── store.js │ ├── pages │ │ ├── home.css │ │ ├── Login.jsx │ │ ├── Regsiter.jsx │ │ ├── Home.jsx │ │ └── Chat.jsx │ ├── redux │ │ ├── profileSlice.js │ │ ├── activeUserSlice.js │ │ ├── searchSlice.js │ │ └── chatsSlice.js │ ├── App.js │ ├── apis │ │ ├── messages.js │ │ ├── auth.js │ │ └── chat.js │ ├── index.js │ └── utils │ │ └── logics.js ├── public │ ├── logo.png │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── tailwind.config.js ├── .gitignore ├── package.json └── README.md ├── .gitignore ├── package.json ├── License.md └── README.md /server/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.env -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } -------------------------------------------------------------------------------- /clients/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /clients/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozen-dev71/Realtime-Chat/main/clients/public/logo.png -------------------------------------------------------------------------------- /clients/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /clients/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozen-dev71/Realtime-Chat/main/clients/public/favicon.ico -------------------------------------------------------------------------------- /clients/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozen-dev71/Realtime-Chat/main/clients/public/logo192.png -------------------------------------------------------------------------------- /clients/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozen-dev71/Realtime-Chat/main/clients/public/logo512.png -------------------------------------------------------------------------------- /clients/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozen-dev71/Realtime-Chat/main/clients/src/assets/logo.png -------------------------------------------------------------------------------- /clients/src/assets/no-contacts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozen-dev71/Realtime-Chat/main/clients/src/assets/no-contacts.jpg -------------------------------------------------------------------------------- /clients/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{js,jsx,ts,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /clients/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /clients/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /server/routes/message.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | const router = express.Router(); 3 | import { sendMessage, getMessages } from "../controllers/messageControllers.js"; 4 | import { Auth } from "../middleware/user.js"; 5 | router.post("/", Auth, sendMessage); 6 | router.get("/:chatId", Auth, getMessages); 7 | export default router; 8 | -------------------------------------------------------------------------------- /clients/src/components/ui/SkeletonLoading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Skeleton from 'react-loading-skeleton' 3 | import 'react-loading-skeleton/dist/skeleton.css' 4 | 5 | function SkeletonLoading({ height, count }) { 6 | return ( 7 |
8 | 9 |
10 | ) 11 | } 12 | 13 | export default SkeletonLoading -------------------------------------------------------------------------------- /server/mongoDB/connection.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | const mongoDBConnect = () => { 3 | try { 4 | mongoose.connect(process.env.URL, { 5 | useUnifiedTopology: true, 6 | useNewUrlParser: true, 7 | }); 8 | console.log("MongoDB - Connected"); 9 | } catch (error) { 10 | console.log("Error - MongoDB Connection " + error); 11 | } 12 | }; 13 | export default mongoDBConnect; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | .env 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /clients/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | .env 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /clients/src/components/ui/Typing.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Typing({ className, width, height }) { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | 11 | export default Typing -------------------------------------------------------------------------------- /clients/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /clients/src/components/ui/Loading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | 11 | export default Loading -------------------------------------------------------------------------------- /clients/src/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import activeUserSlice from "./redux/activeUserSlice"; 3 | import chatsSlice from "./redux/chatsSlice"; 4 | import profileSlice from "./redux/profileSlice"; 5 | import searchSlice from "./redux/searchSlice"; 6 | const store = configureStore({ 7 | reducer: { 8 | activeUser: activeUserSlice, 9 | profile: profileSlice, 10 | search: searchSlice, 11 | chats: chatsSlice, 12 | }, 13 | }); 14 | export default store; 15 | -------------------------------------------------------------------------------- /server/models/messageModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | const messageSchema = mongoose.Schema( 3 | { 4 | sender: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | ref: "User", 7 | }, 8 | message: { 9 | type: String, 10 | trim: true, 11 | }, 12 | chatId: { 13 | type: mongoose.Schema.Types.ObjectId, 14 | ref: "Chat", 15 | }, 16 | }, 17 | { 18 | timestamps: true, 19 | } 20 | ); 21 | const messageModel = mongoose.model("Message", messageSchema); 22 | export default messageModel; 23 | -------------------------------------------------------------------------------- /clients/src/pages/home.css: -------------------------------------------------------------------------------- 1 | .alert-enter { 2 | opacity: 0; 3 | transform: scale(0.9); 4 | } 5 | .alert-enter-active { 6 | opacity: 1; 7 | transform: translateX(0); 8 | transition: opacity 300ms, transform 300ms; 9 | } 10 | .alert-exit { 11 | opacity: 1; 12 | } 13 | .alert-exit-active { 14 | opacity: 0; 15 | transform: scale(0.9); 16 | transition: opacity 300ms, transform 300ms; 17 | } 18 | .scrollbar-hide::-webkit-scrollbar { 19 | display: none; 20 | } 21 | @media screen and (max-width: 1023px) { 22 | .chat-page { 23 | min-width: 600px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /clients/src/components/ui/NoContacts.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import nocontacts from "../../assets/no-contacts.jpg" 3 | function NoContacts() { 4 | return ( 5 |
6 | No Contacts 7 |

No Contacts Yet

8 | Search for people 9 |
10 | ) 11 | } 12 | 13 | export default NoContacts -------------------------------------------------------------------------------- /clients/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /clients/src/redux/profileSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | const initialState = { 3 | showProfile: false, 4 | showNotifications: false, 5 | }; 6 | const profileSlice = createSlice({ 7 | name: "profile", 8 | initialState, 9 | reducers: { 10 | setShowProfile: (state, { payload }) => { 11 | state.showProfile = payload; 12 | }, 13 | setShowNotifications: (state, { payload }) => { 14 | state.showNotifications = payload; 15 | }, 16 | }, 17 | }); 18 | export const { setShowProfile, setShowNotifications } = profileSlice.actions; 19 | export default profileSlice.reducer; 20 | -------------------------------------------------------------------------------- /server/routes/chat.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { Auth } from '../middleware/user.js'; 3 | const router = express.Router(); 4 | 5 | import { 6 | accessChats, 7 | fetchAllChats, 8 | creatGroup, 9 | renameGroup, 10 | addToGroup, 11 | removeFromGroup, 12 | } from '../controllers/chatControllers.js'; 13 | router.post('/', Auth, accessChats); 14 | router.get('/', Auth, fetchAllChats); 15 | router.post('/group', Auth, creatGroup); 16 | router.patch('/group/rename', Auth, renameGroup); 17 | router.patch('/groupAdd', Auth, addToGroup); 18 | router.patch('/groupRemove', Auth, removeFromGroup); 19 | router.delete('/removeuser', Auth); 20 | 21 | export default router; 22 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcryptjs": "^2.4.3", 14 | "body-parser": "^1.20.1", 15 | "cookie-parser": "^1.4.6", 16 | "cors": "^2.8.5", 17 | "dotenv": "^16.0.3", 18 | "express": "^4.18.2", 19 | "google-auth-library": "^8.7.0", 20 | "jsonwebtoken": "^9.0.0", 21 | "jwt-check-expiration": "^1.0.5", 22 | "jwt-decode": "^3.1.2", 23 | "mongoose": "^6.8.2", 24 | "socket.io": "^4.6.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /clients/src/App.js: -------------------------------------------------------------------------------- 1 | import Login from './pages/Login'; 2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; 3 | import Regsiter from './pages/Regsiter'; 4 | import Home from './pages/Home'; 5 | import Start from './components/Start'; 6 | 7 | function App() { 8 | return ( 9 |
10 | 11 | 12 | } /> 13 | } /> 14 | } /> 15 | } /> 16 | 17 | 18 |
19 | ); 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /server/routes/user.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { 3 | register, 4 | login, 5 | validUser, 6 | googleAuth, 7 | logout, 8 | searchUsers, 9 | updateInfo, 10 | getUserById, 11 | } from '../controllers/user.js'; 12 | import { Auth } from '../middleware/user.js'; 13 | const router = express.Router(); 14 | router.post('/auth/register', register); 15 | router.post('/auth/login', login); 16 | router.get('/auth/valid', Auth, validUser); 17 | router.get('/auth/logout', Auth, logout); 18 | router.post('/api/google', googleAuth); 19 | router.get('/api/user?', Auth, searchUsers); 20 | router.get('/api/users/:id', Auth, getUserById); 21 | router.patch('/api/users/update/:id', Auth, updateInfo); 22 | export default router; 23 | -------------------------------------------------------------------------------- /clients/src/apis/messages.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | const API = (token) => 3 | axios.create({ 4 | baseURL: process.env.REACT_APP_SERVER_URL, 5 | headers: { Authorization: token }, 6 | }); 7 | export const sendMessage = async (body) => { 8 | try { 9 | const token = localStorage.getItem('userToken'); 10 | const { data } = await API(token).post('/api/message/', body); 11 | return data; 12 | } catch (error) { 13 | console.log('error in sendmessage api' + error); 14 | } 15 | }; 16 | export const fetchMessages = async (id) => { 17 | try { 18 | const token = localStorage.getItem('userToken'); 19 | 20 | const { data } = await API(token).get(`/api/message/${id}`); 21 | return data; 22 | } catch (error) { 23 | console.log('error in fetch Message API ' + error); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /clients/src/redux/activeUserSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | const initialState = { 4 | id: '', 5 | email: '', 6 | profilePic: '', 7 | bio: '', 8 | name: '', 9 | }; 10 | 11 | const activeUserSlice = createSlice({ 12 | name: 'activeUser', 13 | initialState, 14 | reducers: { 15 | setActiveUser: (state, { payload }) => { 16 | state.id = payload.id; 17 | state.email = payload.email; 18 | state.profilePic = payload.profilePic; 19 | state.bio = payload.bio; 20 | state.name = payload.name; 21 | }, 22 | setUserNameAndBio: (state, { payload }) => { 23 | state.name = payload.name; 24 | state.bio = payload.bio; 25 | }, 26 | }, 27 | }); 28 | export const { setActiveUser, setUserNameAndBio } = activeUserSlice.actions; 29 | export default activeUserSlice.reducer; 30 | -------------------------------------------------------------------------------- /server/models/chatModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | const chatSchema = mongoose.Schema( 3 | { 4 | photo: { 5 | type: String, 6 | default: 'https://cdn-icons-png.flaticon.com/512/9790/9790561.png', 7 | }, 8 | chatName: { 9 | type: String, 10 | }, 11 | isGroup: { 12 | type: Boolean, 13 | default: false, 14 | }, 15 | users: [ 16 | { 17 | type: mongoose.Schema.Types.ObjectId, 18 | ref: 'User', 19 | }, 20 | ], 21 | latestMessage: { 22 | type: mongoose.Schema.Types.ObjectId, 23 | ref: 'Message', 24 | }, 25 | groupAdmin: { 26 | type: mongoose.Schema.Types.ObjectId, 27 | ref: 'User', 28 | }, 29 | }, 30 | { 31 | timestamps: true, 32 | } 33 | ); 34 | const chatModel = mongoose.model('Chat', chatSchema); 35 | export default chatModel; 36 | -------------------------------------------------------------------------------- /clients/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import './index.css'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import { Provider } from 'react-redux'; 7 | import store from './store'; 8 | import { ToastContainer } from 'react-toastify'; 9 | import 'react-toastify/dist/ReactToastify.css'; 10 | const root = ReactDOM.createRoot(document.getElementById('root')); 11 | root.render( 12 | 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-website", 3 | "version": "1.0.0", 4 | "description": "This is a real-time chat website that allows users to connect with each other and chat in real-time. It was built using the MERN stack (MongoDB, Express.js, React.js, and Node.js), Socket.io, Redux Toolkit, and Tailwind CSS.", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@babel/cli": "^7.22.9", 9 | "@babel/core": "^7.22.9", 10 | "@babel/preset-env": "^7.22.9", 11 | "@babel/preset-react": "^7.22.5", 12 | "babel-loader": "^9.1.3", 13 | "prettier": "^3.0.0" 14 | }, 15 | "scripts": { 16 | "test": "echo \"Error: no test specified\" && exit 1" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/ShakirFarhan/Realtime-Chat.git" 21 | }, 22 | "author": "Shakir Farhan", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/ShakirFarhan/Realtime-Chat/issues" 26 | }, 27 | "homepage": "https://github.com/ShakirFarhan/Realtime-Chat#readme" 28 | } 29 | -------------------------------------------------------------------------------- /clients/src/components/Start.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { validUser } from '../apis/auth' 3 | import { useNavigate } from "react-router-dom" 4 | function Start() { 5 | const pageRoute = useNavigate() 6 | useEffect(() => { 7 | const isValid = async () => { 8 | const data = await validUser() 9 | if (!data?.user) { 10 | pageRoute("/login") 11 | } 12 | else { 13 | pageRoute("/chats") 14 | 15 | } 16 | } 17 | isValid() 18 | 19 | }, [pageRoute]) 20 | return ( 21 | 22 | 23 |
24 | 25 |

Please Wait. It Might take some time

26 |
27 | 28 | ) 29 | } 30 | 31 | export default Start -------------------------------------------------------------------------------- /clients/src/redux/searchSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; 2 | import { toast } from 'react-toastify'; 3 | import { searchUsers } from '../apis/auth'; 4 | const initialState = { 5 | searchResults: [], 6 | isLoading: false, 7 | isError: false, 8 | }; 9 | export const searchUserThunk = createAsyncThunk( 10 | 'redux/searchUser', 11 | async (search) => { 12 | try { 13 | const { data } = await searchUsers(search); 14 | return data; 15 | } catch (error) { 16 | toast.error('Something Went Wrong.Try Again!'); 17 | } 18 | } 19 | ); 20 | const searchSlice = createSlice({ 21 | name: 'search', 22 | initialState, 23 | reducers: {}, 24 | extraReducers: { 25 | [searchUserThunk.pending]: (state) => { 26 | state.isLoading = true; 27 | }, 28 | [searchUserThunk.fulfilled]: (state, { payload }) => { 29 | state.searchResults = payload; 30 | state.isLoading = false; 31 | }, 32 | [searchUserThunk.rejected]: (state) => { 33 | state.isError = true; 34 | }, 35 | }, 36 | }); 37 | export default searchSlice.reducer; 38 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Shakir Farhan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/middleware/user.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import user from '../models/userModel.js'; 3 | 4 | export const Auth = async (req, res, next) => { 5 | try { 6 | let token = req.headers.authorization.split(' ')[0]; //when using browser this line 7 | // let token = req.headers.authorization.split(' ')[1]; //when using postman this line 8 | if (token.length < 500) { 9 | const verifiedUser = jwt.verify(token, process.env.SECRET); 10 | const rootUser = await user 11 | .findOne({ _id: verifiedUser.id }) 12 | .select('-password'); 13 | req.token = token; 14 | req.rootUser = rootUser; 15 | req.rootUserId = rootUser._id; 16 | } else { 17 | let data = jwt.decode(token); 18 | req.rootUserEmail = data.email; 19 | const googleUser = await user 20 | .findOne({ email: req.rootUserEmail }) 21 | .select('-password'); 22 | req.rootUser = googleUser; 23 | req.token = token; 24 | req.rootUserId = googleUser._id; 25 | } 26 | next(); 27 | } catch (error) { 28 | // console.log(error); 29 | res.json({ error: 'Invalid Token' }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /clients/src/redux/chatsSlice.js: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; 2 | import { toast } from 'react-toastify'; 3 | import { fetchAllChats } from '../apis/chat'; 4 | const initialState = { 5 | chats: [], 6 | activeChat: '', 7 | isLoading: false, 8 | notifications: [], 9 | }; 10 | export const fetchChats = createAsyncThunk('redux/chats', async () => { 11 | try { 12 | const data = await fetchAllChats(); 13 | return data; 14 | } catch (error) { 15 | toast.error('Something Went Wrong!Try Again'); 16 | } 17 | }); 18 | const chatsSlice = createSlice({ 19 | name: 'chats', 20 | initialState, 21 | reducers: { 22 | setActiveChat: (state, { payload }) => { 23 | state.activeChat = payload; 24 | }, 25 | setNotifications: (state, { payload }) => { 26 | state.notifications = payload; 27 | }, 28 | }, 29 | extraReducers: { 30 | [fetchChats.pending]: (state) => { 31 | state.isLoading = true; 32 | }, 33 | [fetchChats.fulfilled]: (state, { payload }) => { 34 | state.chats = payload; 35 | state.isLoading = false; 36 | }, 37 | [fetchChats.rejected]: (state) => { 38 | state.isLoading = false; 39 | }, 40 | }, 41 | }); 42 | export const { setActiveChat, setNotifications } = chatsSlice.actions; 43 | export default chatsSlice.reducer; 44 | -------------------------------------------------------------------------------- /server/controllers/messageControllers.js: -------------------------------------------------------------------------------- 1 | import Message from '../models/messageModel.js'; 2 | import user from '../models/userModel.js'; 3 | import Chat from '../models/chatModel.js'; 4 | export const sendMessage = async (req, res) => { 5 | const { chatId, message } = req.body; 6 | try { 7 | let msg = await Message.create({ sender: req.rootUserId, message, chatId }); 8 | msg = await ( 9 | await msg.populate('sender', 'name profilePic email') 10 | ).populate({ 11 | path: 'chatId', 12 | select: 'chatName isGroup users', 13 | model: 'Chat', 14 | populate: { 15 | path: 'users', 16 | select: 'name email profilePic', 17 | model: 'User', 18 | }, 19 | }); 20 | await Chat.findByIdAndUpdate(chatId, { 21 | latestMessage: msg, 22 | }); 23 | res.status(200).send(msg); 24 | } catch (error) { 25 | console.log(error); 26 | res.status(500).json({ error: error }); 27 | } 28 | }; 29 | export const getMessages = async (req, res) => { 30 | const { chatId } = req.params; 31 | try { 32 | let messages = await Message.find({ chatId }) 33 | .populate({ 34 | path: 'sender', 35 | model: 'User', 36 | select: 'name profilePic email', 37 | }) 38 | .populate({ 39 | path: 'chatId', 40 | model: 'Chat', 41 | }); 42 | 43 | res.status(200).json(messages); 44 | } catch (error) { 45 | res.sendStatus(500).json({ error: error }); 46 | console.log(error); 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /server/models/userModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import bcrypt from 'bcryptjs'; 3 | import jwt from 'jsonwebtoken'; 4 | const userSchema = mongoose.Schema( 5 | { 6 | name: { 7 | type: String, 8 | required: true, 9 | }, 10 | email: { 11 | type: String, 12 | required: true, 13 | }, 14 | password: { 15 | type: String, 16 | required: true, 17 | }, 18 | bio: { 19 | type: String, 20 | default: 'Available', 21 | }, 22 | profilePic: { 23 | type: String, 24 | default: 25 | 'https://icon-library.com/images/anonymous-avatar-icon/anonymous-avatar-icon-25.jpg', 26 | }, 27 | contacts: [ 28 | { 29 | type: mongoose.Schema.Types.ObjectId, 30 | ref: 'User', 31 | }, 32 | ], 33 | }, 34 | { 35 | timestamps: true, 36 | } 37 | ); 38 | userSchema.pre('save', async function (next) { 39 | if (this.isModified('password')) { 40 | this.password = await bcrypt.hash(this.password, 12); 41 | } 42 | next(); 43 | }); 44 | userSchema.methods.generateAuthToken = async function () { 45 | try { 46 | let token = jwt.sign( 47 | { id: this._id, email: this.email }, 48 | process.env.SECRET, 49 | { 50 | expiresIn: '24h', 51 | } 52 | ); 53 | 54 | return token; 55 | } catch (error) { 56 | console.log('error while generating token'); 57 | } 58 | }; 59 | 60 | const userModel = mongoose.model('User', userSchema); 61 | export default userModel; 62 | -------------------------------------------------------------------------------- /clients/src/components/group/Search.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SkeletonLoading from '../ui/SkeletonLoading' 3 | import "../../pages/home.css" 4 | function Search({ type, isLoading, searchResults, handleClick, search }) { 5 | 6 | return ( 7 |
8 | 9 | { 10 | isLoading ? : ( 11 | searchResults.length > 0 ? searchResults?.map((e) => { 12 | return ( 13 |
14 |
15 | 16 | 17 |
18 |
{e.name}
19 |
{e.email}
20 |
21 |
22 | 23 |
24 | ) 25 | }) : No results found 26 | ) 27 | 28 | } 29 |
30 | ) 31 | } 32 | 33 | export default Search -------------------------------------------------------------------------------- /clients/src/components/profile/InputEdit.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { TbEdit } from "react-icons/tb" 3 | import { BsCheck2 } from "react-icons/bs" 4 | function InputEdit({ type, handleChange, input, handleSubmit }) { 5 | const [editable, setEditable] = useState(false) 6 | // const [showPicker, setShowPicker] = useState(false) 7 | const submitButton = () => { 8 | handleSubmit() 9 | setEditable(false) 10 | } 11 | return ( 12 | <> 13 |
14 |

Your name

15 | { 16 | !editable ? 17 | 18 |
19 | 20 |

21 | 22 | {input} 23 |

24 | 25 | 28 |
29 | 30 | :
31 | 32 |
33 | 34 |
35 |
36 | 37 | 38 | 41 |
42 |
43 | 44 | } 45 | 46 | 47 |
48 | 49 | ) 50 | } 51 | 52 | export default InputEdit -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import dotenv from 'dotenv/config'; 3 | import mongoDBConnect from './mongoDB/connection.js'; 4 | import mongoose from 'mongoose'; 5 | import bodyParser from 'body-parser'; 6 | import cors from 'cors'; 7 | import userRoutes from './routes/user.js'; 8 | import chatRoutes from './routes/chat.js'; 9 | import messageRoutes from './routes/message.js'; 10 | import * as Server from 'socket.io'; 11 | 12 | const app = express(); 13 | const corsConfig = { 14 | origin: process.env.BASE_URL, 15 | credentials: true, 16 | }; 17 | 18 | app.use(bodyParser.json()); 19 | app.use(bodyParser.urlencoded({ extended: true })); 20 | app.use(cors(corsConfig)); 21 | app.use('/', userRoutes); 22 | app.use('/api/chat', chatRoutes); 23 | app.use('/api/message', messageRoutes); 24 | mongoose.set('strictQuery', false); 25 | mongoDBConnect(); 26 | const server = app.listen(process.env.PORT, () => { 27 | console.log(`Server Listening at PORT - ${process.env.PORT}`); 28 | }); 29 | const io = new Server.Server(server, { 30 | pingTimeout: 60000, 31 | cors: { 32 | origin: 'http://localhost:3000', 33 | }, 34 | }); 35 | io.on('connection', (socket) => { 36 | socket.on('setup', (userData) => { 37 | socket.join(userData.id); 38 | socket.emit('connected'); 39 | }); 40 | socket.on('join room', (room) => { 41 | socket.join(room); 42 | }); 43 | socket.on('typing', (room) => socket.in(room).emit('typing')); 44 | socket.on('stop typing', (room) => socket.in(room).emit('stop typing')); 45 | 46 | socket.on('new message', (newMessageRecieve) => { 47 | var chat = newMessageRecieve.chatId; 48 | if (!chat.users) console.log('chats.users is not defined'); 49 | chat.users.forEach((user) => { 50 | if (user._id == newMessageRecieve.sender._id) return; 51 | socket.in(user._id).emit('message recieved', newMessageRecieve); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /clients/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clients", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@chakra-ui/react": "^2.7.1", 7 | "@emoji-mart/data": "^1.1.2", 8 | "@emoji-mart/react": "^1.1.1", 9 | "@emotion/react": "^11.11.1", 10 | "@emotion/styled": "^11.11.0", 11 | "@mui/material": "^5.11.4", 12 | "@reduxjs/toolkit": "^1.9.1", 13 | "@testing-library/jest-dom": "^5.16.5", 14 | "@testing-library/react": "^13.4.0", 15 | "@testing-library/user-event": "^13.5.0", 16 | "axios": "^1.2.2", 17 | "dotenv": "^16.0.3", 18 | "emoji-mart": "^5.5.2", 19 | "framer-motion": "^10.12.18", 20 | "gapi-script": "^1.2.0", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0", 23 | "react-google-login": "^5.2.2", 24 | "react-icons": "^4.7.1", 25 | "react-loading-skeleton": "^3.2.0", 26 | "react-notification-badge": "^1.5.1", 27 | "react-redux": "^8.0.5", 28 | "react-router": "^6.9.0", 29 | "react-router-dom": "^6.6.2", 30 | "react-scripts": "5.0.1", 31 | "react-scrollable-feed": "^1.3.1", 32 | "react-toastify": "^9.1.1", 33 | "react-transition-group": "^4.4.5", 34 | "socket.io-client": "^4.5.4", 35 | "web-vitals": "^2.1.4" 36 | }, 37 | "scripts": { 38 | "start": "react-scripts start", 39 | "build": "react-scripts build", 40 | "test": "react-scripts test", 41 | "eject": "react-scripts eject" 42 | }, 43 | "eslintConfig": { 44 | "extends": [ 45 | "react-app", 46 | "react-app/jest" 47 | ] 48 | }, 49 | "browserslist": { 50 | "production": [ 51 | ">0.2%", 52 | "not dead", 53 | "not op_mini all" 54 | ], 55 | "development": [ 56 | "last 1 chrome version", 57 | "last 1 firefox version", 58 | "last 1 safari version" 59 | ] 60 | }, 61 | "devDependencies": { 62 | "tailwindcss": "^3.2.4" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /clients/src/apis/auth.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { toast } from 'react-toastify'; 3 | const API = (token) => 4 | axios.create({ 5 | baseURL: process.env.REACT_APP_SERVER_URL, 6 | headers: { Authorization: token }, 7 | }); 8 | let url = process.env.REACT_APP_SERVER_URL; 9 | export const loginUser = async (body) => { 10 | try { 11 | return await axios.post(`${url}/auth/login`, body); 12 | } catch (error) { 13 | console.log('error in loginuser api'); 14 | } 15 | }; 16 | export const googleAuth = async (body) => { 17 | try { 18 | return await axios.post(`${url}/api/google`, body); 19 | } catch (error) { 20 | console.log(error); 21 | } 22 | }; 23 | export const registerUser = async (body) => { 24 | try { 25 | return await axios.post(`${url}/auth/register`, body); 26 | } catch (error) { 27 | console.log('error in register api'); 28 | } 29 | }; 30 | export const validUser = async () => { 31 | try { 32 | const token = localStorage.getItem('userToken'); 33 | 34 | const { data } = await API(token).get(`/auth/valid`, { 35 | headers: { Authorization: token }, 36 | }); 37 | return data; 38 | } catch (error) { 39 | console.log('error in valid user api'); 40 | } 41 | }; 42 | export const searchUsers = async (id) => { 43 | try { 44 | const token = localStorage.getItem('userToken'); 45 | 46 | return await API(token).get(`/api/user?search=${id}`); 47 | } catch (error) { 48 | console.log('error in search users api'); 49 | } 50 | }; 51 | export const updateUser = async (id, body) => { 52 | try { 53 | const token = localStorage.getItem('userToken'); 54 | 55 | const { data } = await API(token).patch(`/api/users/update/${id}`, body); 56 | return data; 57 | } catch (error) { 58 | console.log('error in update user api'); 59 | toast.error('Something Went Wrong.try Again!'); 60 | } 61 | }; 62 | export const checkValid = async () => { 63 | const data = await validUser(); 64 | if (!data?.user) { 65 | window.location.href = '/login'; 66 | } else { 67 | window.location.href = '/chats'; 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /clients/src/apis/chat.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { toast } from 'react-toastify'; 3 | const API = (token) => 4 | axios.create({ 5 | baseURL: process.env.REACT_APP_SERVER_URL, 6 | headers: { Authorization: token }, 7 | }); 8 | export const acessCreate = async (body) => { 9 | try { 10 | const token = localStorage.getItem('userToken'); 11 | 12 | const { data } = await API(token).post('/api/chat', body); 13 | console.log(data); 14 | return data; 15 | } catch (error) { 16 | console.log('error in access create api'); 17 | } 18 | }; 19 | export const fetchAllChats = async () => { 20 | try { 21 | const token = localStorage.getItem('userToken'); 22 | const { data } = await API(token).get('/api/chat'); 23 | return data; 24 | } catch (error) { 25 | console.log('error in fetch all chats api'); 26 | } 27 | }; 28 | export const createGroup = async (body) => { 29 | try { 30 | const token = localStorage.getItem('userToken'); 31 | const { data } = await API(token).post('/api/chat/group', body); 32 | toast.success(`${data.chatName} Group Created`); 33 | return data; 34 | } catch (error) { 35 | console.log('error in create group api'); 36 | } 37 | }; 38 | export const addToGroup = async (body) => { 39 | try { 40 | const token = localStorage.getItem('userToken'); 41 | const { data } = await API(token).patch('/api/chat/groupAdd', body); 42 | return data; 43 | } catch (error) { 44 | console.log('error in addtogroup api'); 45 | } 46 | }; 47 | export const renameGroup = async (body) => { 48 | try { 49 | const token = localStorage.getItem('userToken'); 50 | const { data } = await API(token).patch('/api/chat/group/rename', body); 51 | return data; 52 | } catch (error) { 53 | console.log('error in rename group api'); 54 | } 55 | }; 56 | export const removeUser = async (body) => { 57 | try { 58 | const token = localStorage.getItem('userToken'); 59 | const { data } = await API(token).patch('/api/chat/groupRemove', body); 60 | return data; 61 | } catch (error) { 62 | console.log('error in remove user api'); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /clients/src/components/MessageHistory.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSelector } from 'react-redux' 3 | import ScrollableFeed from "react-scrollable-feed" 4 | import { isSameSender, isSameSenderMargin, isSameUser, isLastMessage } from '../utils/logics' 5 | import { Tooltip } from "@chakra-ui/tooltip"; 6 | import { Avatar } from "@chakra-ui/avatar"; 7 | import "../pages/home.css" 8 | function MessageHistory({ messages }) { 9 | const activeUser = useSelector((state) => state.activeUser) 10 | 11 | return ( 12 | <> 13 | 14 | {messages && 15 | messages.map((m, i) => ( 16 | 17 |
18 | {(isSameSender(messages, m, i, activeUser.id) || 19 | isLastMessage(messages, i, activeUser.id)) && ( 20 | 21 | 31 | 32 | 33 | )} 34 | 46 | {m.message} 47 | 48 |
49 | )) 50 | } 51 | 52 |
53 | 54 | ) 55 | } 56 | 57 | export default MessageHistory -------------------------------------------------------------------------------- /clients/src/components/Contacts.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSelector, useDispatch } from 'react-redux' 3 | import { setActiveChat, fetchChats } from '../redux/chatsSlice' 4 | import { useEffect } from 'react' 5 | import { getChatName, getChatPhoto, timeSince } from '../utils/logics' 6 | import NoContacts from './ui/NoContacts' 7 | // import SkeletonLoading from './ui/SkeletonLoading' 8 | var aDay = 24 * 60 * 60 * 1000; 9 | function Contacts() { 10 | const { chats, activeChat } = useSelector((state) => state.chats) 11 | const dispatch = useDispatch() 12 | const activeUser = useSelector((state) => state.activeUser) 13 | useEffect(() => { 14 | dispatch(fetchChats()) 15 | }, [dispatch]) 16 | return ( 17 | <> 18 |
19 | { 20 | chats?.length > 0 ? chats?.map((e) => { 21 | return ( 22 |
{ 23 | dispatch(setActiveChat(e)) 24 | }} key={e._id} className={`flex items-center justify-between sm:gap-x-1 md:gap-x-1 mt-5 ${activeChat._id === e._id ? "bg-[#fafafa]" : "bg-[#fff]"} cursor-pointer py-4 px-2`}> 25 |
26 | 27 |
28 |
{getChatName(e, activeUser)}
29 |

{e.latestMessage?.message.length > 30 30 | ? e.latestMessage?.message.slice(0, 30) + "..." 31 | : e.latestMessage?.message 32 | }

33 |
34 |
35 |
36 |

{timeSince(new Date(Date.parse(e.updatedAt) - aDay))}

37 |
38 |
39 | ) 40 | }) : 41 | } 42 |
43 | 44 | 45 | ) 46 | } 47 | 48 | export default Contacts -------------------------------------------------------------------------------- /clients/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 35 | Talk Time 36 | 41 | 42 | 43 | 44 |
45 | 46 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /clients/src/utils/logics.js: -------------------------------------------------------------------------------- 1 | export const isSameSenderMargin = (messages, m, i, userId) => { 2 | if ( 3 | i < messages.length - 1 && 4 | messages[i + 1].sender._id === m.sender._id && 5 | messages[i].sender._id !== userId 6 | ) 7 | return 33; 8 | else if ( 9 | (i < messages.length - 1 && 10 | messages[i + 1].sender._id !== m.sender._id && 11 | messages[i].sender._id !== userId) || 12 | (i === messages.length - 1 && messages[i].sender._id !== userId) 13 | ) 14 | return 0; 15 | else return 'auto'; 16 | }; 17 | export function timeSince(date) { 18 | var seconds = Math.floor((new Date() - date) / 1000); 19 | 20 | var interval = seconds / 31536000; 21 | 22 | if (interval > 1) { 23 | return Math.floor(interval) + ' year ago'; 24 | } 25 | interval = seconds / 2592000; 26 | if (interval > 1) { 27 | return Math.floor(interval) + ' month ago'; 28 | } 29 | interval = seconds / 86400; 30 | if (interval > 1) { 31 | return Math.floor(interval) + ' day ago'; 32 | } 33 | interval = seconds / 3600; 34 | if (interval > 1) { 35 | return Math.floor(interval) + ' hour ago'; 36 | } 37 | interval = seconds / 60; 38 | if (interval > 1) { 39 | return Math.floor(interval) + ' minute ago'; 40 | } 41 | return Math.floor(seconds) + ' seconda ago'; 42 | } 43 | export const isSameSender = (messages, m, i, userId) => { 44 | return ( 45 | i < messages.length - 1 && 46 | (messages[i + 1].sender._id !== m.sender._id || 47 | messages[i + 1].sender._id === undefined) && 48 | messages[i].sender._id !== userId 49 | ); 50 | }; 51 | export const isLastMessage = (messages, i, userId) => { 52 | return ( 53 | i === messages.length - 1 && 54 | messages[messages.length - 1].sender._id !== userId && 55 | messages[messages.length - 1].sender._id 56 | ); 57 | }; 58 | export const isSameUser = (messages, m, i) => { 59 | return i > 0 && messages[i - 1].sender._id === m.sender._id; 60 | }; 61 | export const getSender = (activeUser, users) => { 62 | return activeUser.id === users[0]._id ? users[1].name : users[0].name; 63 | }; 64 | export const getChatName = (activeChat, activeUser) => { 65 | return activeChat?.isGroup 66 | ? activeChat?.chatName 67 | : activeChat?.users[0]?._id === activeUser.id 68 | ? activeChat?.users[1]?.name 69 | : activeChat?.users[0]?.name; 70 | }; 71 | export const getChatPhoto = (activeChat, activeUser) => { 72 | return activeChat?.isGroup 73 | ? activeChat.photo 74 | : activeChat?.users[0]?._id === activeUser?.id 75 | ? activeChat?.users[1]?.profilePic 76 | : activeChat?.users[0]?.profilePic; 77 | }; 78 | -------------------------------------------------------------------------------- /clients/src/components/Profile.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { IoArrowBack } from "react-icons/io5" 3 | import { useDispatch, useSelector } from 'react-redux' 4 | import { setShowProfile } from '../redux/profileSlice' 5 | import { IoMdLogOut } from "react-icons/io" 6 | import InputEdit from './profile/InputEdit' 7 | import { updateUser } from '../apis/auth' 8 | import { toast } from 'react-toastify' 9 | import { setUserNameAndBio } from '../redux/activeUserSlice' 10 | function Profile(props) { 11 | const dispatch = useDispatch() 12 | const { showProfile } = useSelector((state) => state.profile) 13 | const activeUser = useSelector((state) => state.activeUser) 14 | const [formData, setFormData] = useState({ 15 | name: activeUser.name, 16 | bio: activeUser.bio 17 | }) 18 | const logoutUser = () => { 19 | toast.success("Logout Successfull!") 20 | localStorage.removeItem("userToken") 21 | window.location.href = "/login" 22 | } 23 | const handleChange = (e) => { 24 | setFormData({ ...formData, [e.target.name]: e.target.value }) 25 | } 26 | const submit = async () => { 27 | 28 | dispatch(setUserNameAndBio(formData)) 29 | toast.success("Updated!") 30 | await updateUser(activeUser.id, formData) 31 | 32 | } 33 | 34 | return ( 35 | 36 |
37 |
38 |
39 | 43 |
44 |
45 |
46 | 47 |
48 | 49 | 50 |
51 | 52 |
53 |

54 | This is not your username or pin. This name will be visible to your contacts 55 |

56 |
57 | 58 |
59 | 60 |
61 | 62 |
63 | 64 |
Logout
65 |
66 |
67 |
68 | ) 69 | } 70 | 71 | export default Profile -------------------------------------------------------------------------------- /clients/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Real-Time Chat Website with MERN Stack, Socket.io, Redux Toolkit, and Tailwind CSS 2 | 3 | This is a real-time chat website that allows users to connect with each other and chat in real-time. It was built using the MERN stack (MongoDB, Express.js, React.js, and Node.js), Socket.io, Redux Toolkit, and Tailwind CSS. 4 | 5 | - If you liked it then give this Repository a Star⭐ 6 | - Youtube Demo : Click On Me 7 | 8 | ## Technologies Used 9 | 10 | - MERN stack (MongoDB, Express.js, React.js, and Node.js) 11 | - Socket.io 12 | - Redux Toolkit 13 | - Tailwind CSS 14 | 15 | ## Features 16 | 17 | - Real-time chat: users can send and receive messages in real-time 18 | - User authentication: users can sign up, log in, and log out using JWT and Google Auth 19 | - Group creation: users can create chat rooms and invite others to join 20 | - Notifications: users can receive notifications on new messages 21 | - Emojis: users can send and receive emojis in messages 22 | - Profile page where users can update their avatar and display name. 23 | - Users can create a room to chat with others. 24 | - Search functionality. 25 | - Responsive design: the website is optimized for different screen sizes and devices 26 | 27 | ## Configuration and Setup 28 | In order to run this project locally, simply fork and clone the repository or download as zip and unzip on your machine. 29 | 30 | - Open the project in your prefered code editor. 31 | - Go to terminal -> New terminal (If you are using VS code) 32 | - Split your terminal into two (run the client on one terminal and the server on the other terminal) 33 | 34 | In the first terminal 35 | - cd client and create a .env file in the root of your client directory. 36 | - Supply the following credentials 37 | 38 | ``` 39 | REACT_APP_GOOGLE_CLIENT_ID = 40 | REACT_APP_SERVER_URL='http://localhost:8000' 41 | ``` 42 | 43 | To get your Google ClientID for authentication, go to the [credential Page ](https://console.cloud.google.com/apis/credentials) (if you are new, then [create a new project first](https://console.cloud.google.com/projectcreate) and follow the following steps; 44 | 45 | - Click Create credentials > OAuth client ID. 46 | - Select the Web application type. 47 | - Name your OAuth client and click Create 48 | - Remember to provide your domain and redirect URL so that Google identifies the origin domain to which it can display the consent screen. In development, that is going to be `http://localhost:3000` and `http://localhost:3000/login` 49 | - Copy the Client ID and assign it to the variable `REACT_APP_GOOGLE_CLIENT_ID` in your .env file 50 | 51 | ``` 52 | $ cd client 53 | $ npm install (to install client-side dependencies) 54 | $ npm start (to start the client) 55 | ``` 56 | In the second terminal 57 | - cd server and create a .env file in the root of your server directory. 58 | - Supply the following credentials 59 | 60 | ``` 61 | PORT=8000 62 | URL= 63 | SECRET= 64 | CLIENT_ID= 65 | BASE_URL="http://localhost:3000" 66 | ``` 67 | ``` 68 | $ cd server 69 | $ npm install (to install server-side dependencies) 70 | & npm start (to start the server) 71 | ``` 72 | 73 | 74 | ## Contributing 75 | 76 | Contributions to this project are welcome! If you find a bug or want to add a feature, please submit an issue or a pull request. To contribute, follow these steps: 77 | 78 | 1. Fork the repository 79 | 2. Create a new branch for your feature: `git checkout -b my-new-feature` 80 | 3. Make changes and commit them: `git commit -m 'Add some feature'` 81 | 4. Push your branch to your forked repository: `git push origin my-new-feature` 82 | 5. Create a Pull Request 83 | 84 | -------------------------------------------------------------------------------- /server/controllers/chatControllers.js: -------------------------------------------------------------------------------- 1 | import Chat from '../models/chatModel.js'; 2 | import user from '../models/userModel.js'; 3 | 4 | export const accessChats = async (req, res) => { 5 | const { userId } = req.body; 6 | if (!userId) res.send({ message: "Provide User's Id" }); 7 | let chatExists = await Chat.find({ 8 | isGroup: false, 9 | $and: [ 10 | { users: { $elemMatch: { $eq: userId } } }, 11 | { users: { $elemMatch: { $eq: req.rootUserId } } }, 12 | ], 13 | }) 14 | .populate('users', '-password') 15 | .populate('latestMessage'); 16 | chatExists = await user.populate(chatExists, { 17 | path: 'latestMessage.sender', 18 | select: 'name email profilePic', 19 | }); 20 | if (chatExists.length > 0) { 21 | res.status(200).send(chatExists[0]); 22 | } else { 23 | let data = { 24 | chatName: 'sender', 25 | users: [userId, req.rootUserId], 26 | isGroup: false, 27 | }; 28 | try { 29 | const newChat = await Chat.create(data); 30 | const chat = await Chat.find({ _id: newChat._id }).populate( 31 | 'users', 32 | '-password' 33 | ); 34 | res.status(200).json(chat); 35 | } catch (error) { 36 | res.status(500).send(error); 37 | } 38 | } 39 | }; 40 | export const fetchAllChats = async (req, res) => { 41 | try { 42 | const chats = await Chat.find({ 43 | users: { $elemMatch: { $eq: req.rootUserId } }, 44 | }) 45 | .populate('users') 46 | .populate('latestMessage') 47 | .populate('groupAdmin') 48 | .sort({ updatedAt: -1 }); 49 | const finalChats = await user.populate(chats, { 50 | path: 'latestMessage.sender', 51 | select: 'name email profilePic', 52 | }); 53 | res.status(200).json(finalChats); 54 | } catch (error) { 55 | res.status(500).send(error); 56 | console.log(error); 57 | } 58 | }; 59 | export const creatGroup = async (req, res) => { 60 | const { chatName, users } = req.body; 61 | if (!chatName || !users) { 62 | res.status(400).json({ message: 'Please fill the fields' }); 63 | } 64 | const parsedUsers = JSON.parse(users); 65 | if (parsedUsers.length < 2) 66 | res.send(400).send('Group should contain more than 2 users'); 67 | parsedUsers.push(req.rootUser); 68 | try { 69 | const chat = await Chat.create({ 70 | chatName: chatName, 71 | users: parsedUsers, 72 | isGroup: true, 73 | groupAdmin: req.rootUserId, 74 | }); 75 | const createdChat = await Chat.findOne({ _id: chat._id }) 76 | .populate('users', '-password') 77 | .populate('groupAdmin', '-password'); 78 | // res.status(200).json(createdChat); 79 | res.send(createdChat); 80 | } catch (error) { 81 | res.sendStatus(500); 82 | } 83 | }; 84 | export const renameGroup = async (req, res) => { 85 | const { chatId, chatName } = req.body; 86 | if (!chatId || !chatName) 87 | res.status(400).send('Provide Chat id and Chat name'); 88 | try { 89 | const chat = await Chat.findByIdAndUpdate(chatId, { 90 | $set: { chatName }, 91 | }) 92 | .populate('users', '-password') 93 | .populate('groupAdmin', '-password'); 94 | if (!chat) res.status(404); 95 | res.status(200).send(chat); 96 | } catch (error) { 97 | res.status(500).send(error); 98 | console.log(error); 99 | } 100 | }; 101 | export const addToGroup = async (req, res) => { 102 | const { userId, chatId } = req.body; 103 | const existing = await Chat.findOne({ _id: chatId }); 104 | if (!existing.users.includes(userId)) { 105 | const chat = await Chat.findByIdAndUpdate(chatId, { 106 | $push: { users: userId }, 107 | }) 108 | .populate('groupAdmin', '-password') 109 | .populate('users', '-password'); 110 | if (!chat) res.status(404); 111 | res.status(200).send(chat); 112 | } else { 113 | res.status(409).send('user already exists'); 114 | } 115 | }; 116 | export const removeFromGroup = async (req, res) => { 117 | const { userId, chatId } = req.body; 118 | const existing = await Chat.findOne({ _id: chatId }); 119 | if (existing.users.includes(userId)) { 120 | Chat.findByIdAndUpdate(chatId, { 121 | $pull: { users: userId }, 122 | }) 123 | .populate('groupAdmin', '-password') 124 | .populate('users', '-password') 125 | .then((e) => res.status(200).send(e)) 126 | .catch((e) => res.status(404)); 127 | } else { 128 | res.status(409).send('user doesnt exists'); 129 | } 130 | }; 131 | export const removeContact = async (req, res) => {}; 132 | -------------------------------------------------------------------------------- /clients/src/components/Group.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { BsPlusLg } from "react-icons/bs" 3 | import { Modal, Box } from "@mui/material" 4 | import { searchUsers } from '../apis/auth'; 5 | import { RxCross2 } from "react-icons/rx" 6 | import { useEffect } from 'react'; 7 | import { createGroup } from '../apis/chat'; 8 | import { fetchChats } from '../redux/chatsSlice'; 9 | import { useDispatch } from 'react-redux'; 10 | import Search from './group/Search'; 11 | 12 | const style = { 13 | position: 'absolute', 14 | top: '50%', 15 | left: '50%', 16 | transform: 'translate(-50%, -50%)', 17 | minWidth: 400, 18 | bgcolor: 'background.paper', 19 | 20 | boxShadow: 24, 21 | p: 2 22 | 23 | }; 24 | function Group() { 25 | const dispatch = useDispatch() 26 | const [open, setOpen] = useState(false); 27 | const [chatName, setChatName] = useState("") 28 | const [searchResults, setSearchResults] = useState([]) 29 | const [search, setSearch] = useState("") 30 | const [isLoading, setIsLoading] = useState(false) 31 | const [selectedUser, setSelectedUsers] = useState([]) 32 | const handleOpen = () => setOpen(true); 33 | const handleClose = () => { 34 | setOpen(false) 35 | setSearch("") 36 | setSelectedUsers([]) 37 | } 38 | const handleFormSearch = async (e) => { 39 | setSearch(e.target.value) 40 | } 41 | const handleClick = (e) => { 42 | if (selectedUser.includes(e)) { 43 | return 44 | } 45 | setSelectedUsers([...selectedUser, e]) 46 | } 47 | 48 | const deleteSelected = (ele) => { 49 | setSelectedUsers(selectedUser.filter((e) => e._id !== ele._id)) 50 | } 51 | const handleSubmit = async () => { 52 | if (selectedUser.length >= 2) { 53 | 54 | await createGroup({ 55 | chatName, 56 | users: JSON.stringify(selectedUser.map((e) => e._id)) 57 | }) 58 | dispatch(fetchChats()) 59 | handleClose() 60 | } 61 | } 62 | useEffect(() => { 63 | const searchChange = async () => { 64 | setIsLoading(true) 65 | const { data } = await searchUsers(search) 66 | setSearchResults(data) 67 | setIsLoading(false) 68 | } 69 | searchChange() 70 | }, [search]) 71 | useEffect(() => { 72 | }, []) 73 | return ( 74 | <> 75 | 79 | 80 | 81 | 87 | 88 |
Create A Group
89 | 90 |
e.preventDefault()} className='flex flex-col gap-y-3 mt-3'> 91 | 92 | setChatName(e.target.value)} className="border-[#c4ccd5] border-[1px] text-[13.5px] py-[4px] px-2 w-[100%]" type="text" name="chatName" placeholder="Group Name" required /> 93 | 94 |
95 | 96 | { 97 | selectedUser?.map((e) => { 98 | return ( 99 | 103 | ) 104 | }) 105 | } 106 |
107 | 108 | 109 | 110 | 111 |
112 | 113 |
114 | 115 | 116 | 117 |
118 |
119 | 120 | ) 121 | } 122 | 123 | export default Group -------------------------------------------------------------------------------- /server/controllers/user.js: -------------------------------------------------------------------------------- 1 | import user from '../models/userModel.js'; 2 | import bcrypt from 'bcryptjs'; 3 | import { OAuth2Client } from 'google-auth-library'; 4 | export const register = async (req, res) => { 5 | const { firstname, lastname, email, password } = req.body; 6 | try { 7 | const existingUser = await user.findOne({ email }); 8 | if (existingUser) 9 | return res.status(400).json({ error: 'User already Exits' }); 10 | const fullname = firstname + ' ' + lastname; 11 | const newuser = new user({ email, password, name: fullname }); 12 | const token = await newuser.generateAuthToken(); 13 | await newuser.save(); 14 | res.json({ message: 'success', token: token }); 15 | } catch (error) { 16 | console.log('Error in register ' + error); 17 | res.status(500).send(error); 18 | } 19 | }; 20 | export const login = async (req, res) => { 21 | const { email, password } = req.body; 22 | try { 23 | const valid = await user.findOne({ email }); 24 | if (!valid) res.status(200).json({ message: 'User dont exist' }); 25 | const validPassword = await bcrypt.compare(password, valid.password); 26 | if (!validPassword) { 27 | res.status(200).json({ message: 'Invalid Credentials' }); 28 | } else { 29 | const token = await valid.generateAuthToken(); 30 | await valid.save(); 31 | res.cookie('userToken', token, { 32 | httpOnly: true, 33 | maxAge: 24 * 60 * 60 * 1000, 34 | }); 35 | res.status(200).json({ token: token, status: 200 }); 36 | } 37 | } catch (error) { 38 | res.status(500).json({ error: error }); 39 | } 40 | }; 41 | export const validUser = async (req, res) => { 42 | try { 43 | const validuser = await user 44 | .findOne({ _id: req.rootUserId }) 45 | .select('-password'); 46 | if (!validuser) res.json({ message: 'user is not valid' }); 47 | res.status(201).json({ 48 | user: validuser, 49 | token: req.token, 50 | }); 51 | } catch (error) { 52 | res.status(500).json({ error: error }); 53 | console.log(error); 54 | } 55 | }; 56 | export const googleAuth = async (req, res) => { 57 | try { 58 | const { tokenId } = req.body; 59 | const client = new OAuth2Client(process.env.CLIENT_ID); 60 | const verify = await client.verifyIdToken({ 61 | idToken: tokenId, 62 | audience: process.env.CLIENT_ID, 63 | }); 64 | const { email_verified, email, name, picture } = verify.payload; 65 | if (!email_verified) res.json({ message: 'Email Not Verified' }); 66 | const userExist = await user.findOne({ email }).select('-password'); 67 | if (userExist) { 68 | res.cookie('userToken', tokenId, { 69 | httpOnly: true, 70 | maxAge: 24 * 60 * 60 * 1000, 71 | }); 72 | res.status(200).json({ token: tokenId, user: userExist }); 73 | } else { 74 | const password = email + process.env.CLIENT_ID; 75 | const newUser = await user({ 76 | name: name, 77 | profilePic: picture, 78 | password, 79 | email, 80 | }); 81 | await newUser.save(); 82 | res.cookie('userToken', tokenId, { 83 | httpOnly: true, 84 | maxAge: 24 * 60 * 60 * 1000, 85 | }); 86 | res 87 | .status(200) 88 | .json({ message: 'User registered Successfully', token: tokenId }); 89 | } 90 | } catch (error) { 91 | res.status(500).json({ error: error }); 92 | console.log('error in googleAuth backend' + error); 93 | } 94 | }; 95 | 96 | export const logout = (req, res) => { 97 | req.rootUser.tokens = req.rootUser.tokens.filter((e) => e.token != req.token); 98 | }; 99 | export const searchUsers = async (req, res) => { 100 | // const { search } = req.query; 101 | const search = req.query.search 102 | ? { 103 | $or: [ 104 | { name: { $regex: req.query.search, $options: 'i' } }, 105 | { email: { $regex: req.query.search, $options: 'i' } }, 106 | ], 107 | } 108 | : {}; 109 | 110 | const users = await user.find(search).find({ _id: { $ne: req.rootUserId } }); 111 | res.status(200).send(users); 112 | }; 113 | export const getUserById = async (req, res) => { 114 | const { id } = req.params; 115 | try { 116 | const selectedUser = await user.findOne({ _id: id }).select('-password'); 117 | res.status(200).json(selectedUser); 118 | } catch (error) { 119 | res.status(500).json({ error: error }); 120 | } 121 | }; 122 | export const updateInfo = async (req, res) => { 123 | const { id } = req.params; 124 | const { bio, name } = req.body; 125 | const updatedUser = await user.findByIdAndUpdate(id, { name, bio }); 126 | return updatedUser; 127 | }; 128 | -------------------------------------------------------------------------------- /clients/src/pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useEffect } from 'react' 3 | import { GoogleLogin } from "react-google-login" 4 | import { gapi } from "gapi-script" 5 | import { googleAuth } from '../apis/auth' 6 | import { useState } from 'react' 7 | import { loginUser } from '../apis/auth' 8 | import { Link, useNavigate } from 'react-router-dom' 9 | import { BsEmojiLaughing, BsEmojiExpressionless } from "react-icons/bs" 10 | import { toast } from 'react-toastify'; 11 | import { validUser } from '../apis/auth' 12 | const defaultData = { 13 | email: "", 14 | password: "" 15 | } 16 | function Login() { 17 | const [formData, setFormData] = useState(defaultData) 18 | const [isLoading, setIsLoading] = useState(false) 19 | const [showPass, setShowPass] = useState(false) 20 | const pageRoute = useNavigate() 21 | const googleSuccess = async (res) => { 22 | if (res?.profileObj) { 23 | console.log(res.profileObj) 24 | setIsLoading(true) 25 | const response = await googleAuth({ tokenId: res.tokenId }) 26 | setIsLoading(false) 27 | 28 | console.log("response :" + res) 29 | if (response.data.token) { 30 | localStorage.setItem("userToken", response.data.token) 31 | pageRoute("/chats") 32 | 33 | } 34 | } 35 | } 36 | const googleFailure = (error) => { 37 | // toast.error("Something went Wrong.Try Again!") 38 | } 39 | const handleOnChange = (e) => { 40 | setFormData({ ...formData, [e.target.name]: e.target.value }) 41 | } 42 | 43 | const formSubmit = async (e) => { 44 | e.preventDefault() 45 | if (formData.email.includes("@") && formData.password.length > 6) { 46 | setIsLoading(true) 47 | const { data } = await loginUser(formData) 48 | if (data?.token) { 49 | localStorage.setItem("userToken", data.token) 50 | toast.success("Succesfully Login!") 51 | setIsLoading(false) 52 | pageRoute("/chats") 53 | } 54 | else { 55 | setIsLoading(false) 56 | toast.error("Invalid Credentials!") 57 | setFormData({ ...formData, password: "" }) 58 | } 59 | } 60 | else { 61 | setIsLoading(false) 62 | toast.warning("Provide valid Credentials!") 63 | setFormData(defaultData) 64 | 65 | } 66 | } 67 | useEffect(() => { 68 | const initClient = () => { 69 | gapi.client.init({ 70 | clientId: process.env.REACT_APP_CLIENT_ID, 71 | scope: '' 72 | }); 73 | }; 74 | gapi.load('client:auth2', initClient); 75 | const isValid = async () => { 76 | const data = await validUser() 77 | if (data?.user) { 78 | window.location.href = "/chats" 79 | } 80 | 81 | } 82 | isValid() 83 | }, []) 84 | return ( 85 | <> 86 | 87 |
88 |
89 | {/* */} 90 |
91 |

Login

92 |

No Account ? Sign up

93 |
94 | {/*

Login to your Account

*/} 95 |
96 |
97 | 98 | 99 |
100 |
101 | 102 | 103 | { 104 | !showPass ? : 105 | } 106 |
107 | 108 | 115 | {/*
*/} 116 |

/

117 | ( 120 | 124 | )} 125 | onSuccess={googleSuccess} 126 | onFailure={googleFailure} 127 | cookiePolicy={'single_host_origin'} 128 | scope="profile email https://www.googleapis.com/auth/user.birthday.read" 129 | /> 130 | 131 | 132 | 133 |
134 | 135 |
136 | 137 | ) 138 | } 139 | 140 | export default Login -------------------------------------------------------------------------------- /clients/src/pages/Regsiter.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, useNavigate } from 'react-router-dom' 3 | import { GoogleLogin } from "react-google-login" 4 | import { gapi } from "gapi-script" 5 | import { useEffect } from 'react' 6 | import { googleAuth, registerUser } from '../apis/auth' 7 | import { useState } from 'react' 8 | import { BsEmojiLaughing, BsEmojiExpressionless } from "react-icons/bs" 9 | import { toast } from 'react-toastify'; 10 | import { validUser } from '../apis/auth' 11 | const defaultData = { 12 | firstname: "", 13 | lastname: "", 14 | email: "", 15 | password: "" 16 | } 17 | function Regsiter() { 18 | const [formData, setFormData] = useState(defaultData) 19 | const [isLoading, setIsLoading] = useState(false) 20 | const [showPass, setShowPass] = useState(false) 21 | const pageRoute = useNavigate() 22 | const handleOnChange = (e) => { 23 | setFormData({ ...formData, [e.target.name]: e.target.value }) 24 | } 25 | const handleOnSubmit = async (e) => { 26 | e.preventDefault() 27 | setIsLoading(true) 28 | if (formData.email.includes("@") && formData.password.length > 6) { 29 | const { data } = await registerUser(formData) 30 | if (data?.token) { 31 | localStorage.setItem("userToken", data.token) 32 | toast.success("Succesfully Registered😍") 33 | setIsLoading(false) 34 | pageRoute("/chats") 35 | 36 | } 37 | else { 38 | setIsLoading(false) 39 | toast.error("Invalid Credentials!") 40 | } 41 | } 42 | else { 43 | setIsLoading(false) 44 | toast.warning("Provide valid Credentials!") 45 | setFormData({ ...formData, password: "" }) 46 | } 47 | 48 | } 49 | 50 | const googleSuccess = async (res) => { 51 | if (res?.profileObj) { 52 | setIsLoading(true) 53 | const response = await googleAuth({ tokenId: res.tokenId }) 54 | setIsLoading(false) 55 | if (response.data.token) { 56 | localStorage.setItem("userToken", response.data.token) 57 | pageRoute("/chats") 58 | } 59 | } 60 | } 61 | const googleFailure = (error) => { 62 | toast.error("Something Went Wrong.Try Agian!") 63 | } 64 | 65 | useEffect(() => { 66 | const initClient = () => { 67 | gapi.client.init({ 68 | clientId: process.env.REACT_APP_CLIENT_ID, 69 | scope: '' 70 | }); 71 | }; 72 | gapi.load('client:auth2', initClient); 73 | const isValid = async () => { 74 | const data = await validUser() 75 | if (data?.user) { 76 | window.location.href = "/chats" 77 | } 78 | } 79 | isValid() 80 | }, []) 81 | return ( 82 |
83 |
84 |
85 |

Register

86 |

Have Account ? Sign in

87 |
88 |
89 |
90 | 91 | 92 |
93 |
94 | 95 |
96 |
97 | 98 | 99 | 100 | {/* */} 103 | { 104 | !showPass ? : 105 | } 106 | 107 | 108 |
109 | 116 |

/

117 | ( 120 | 124 | )} 125 | onSuccess={googleSuccess} 126 | onFailure={googleFailure} 127 | cookiePolicy={'single_host_origin'} 128 | /> 129 | 130 |
131 |
132 | ) 133 | } 134 | 135 | export default Regsiter -------------------------------------------------------------------------------- /clients/src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { useEffect } from 'react' 3 | import { useDispatch, useSelector } from 'react-redux' 4 | import { searchUsers, validUser } from '../apis/auth' 5 | import { setActiveUser } from '../redux/activeUserSlice' 6 | import { RiNotificationBadgeFill } from "react-icons/ri" 7 | import { BsSearch } from "react-icons/bs" 8 | import { BiNotification } from "react-icons/bi" 9 | import { IoIosArrowDown } from "react-icons/io" 10 | import { setShowNotifications, setShowProfile } from '../redux/profileSlice' 11 | import Chat from './Chat' 12 | import Profile from "../components/Profile" 13 | import { acessCreate } from "../apis/chat.js" 14 | import "./home.css" 15 | import { fetchChats, setNotifications } from '../redux/chatsSlice' 16 | import { getSender } from '../utils/logics' 17 | import { setActiveChat } from '../redux/chatsSlice' 18 | import Group from '../components/Group' 19 | import Contacts from '../components/Contacts' 20 | import { Effect } from "react-notification-badge" 21 | // import NotificationBadge from 'react-notification-badge/lib/components/NotificationBadge'; 22 | import NotificationBadge from 'react-notification-badge'; 23 | import Search from '../components/group/Search' 24 | function Home() { 25 | const dispatch = useDispatch() 26 | const { showProfile, showNotifications } = useSelector((state) => state.profile) 27 | const { notifications } = useSelector((state) => state.chats) 28 | const { activeUser } = useSelector((state) => state) 29 | const [searchResults, setSearchResults] = useState([]) 30 | const [isLoading, setIsLoading] = useState(false) 31 | const [search, setSearch] = useState("") 32 | 33 | const handleSearch = async (e) => { 34 | setSearch(e.target.value) 35 | } 36 | const handleClick = async (e) => { 37 | await acessCreate({ userId: e._id }) 38 | dispatch(fetchChats()) 39 | setSearch("") 40 | } 41 | useEffect(() => { 42 | const searchChange = async () => { 43 | setIsLoading(true) 44 | const { data } = await searchUsers(search) 45 | setSearchResults(data) 46 | setIsLoading(false) 47 | } 48 | searchChange() 49 | }, [search]) 50 | useEffect(() => { 51 | const isValid = async () => { 52 | const data = await validUser() 53 | 54 | const user = { 55 | id: data?.user?._id, 56 | email: data?.user?.email, 57 | profilePic: data?.user?.profilePic, 58 | bio: data?.user?.bio, 59 | name: data?.user?.name 60 | } 61 | dispatch(setActiveUser(user)) 62 | } 63 | isValid() 64 | 65 | }, [dispatch, activeUser]) 66 | 67 | 68 | return ( 69 | <> 70 | 71 |
72 | 73 |
74 | { 75 | !showProfile ? 76 |
77 | 78 |
79 | 85 |
86 | 97 |
98 |
99 | 100 | {!notifications.length && "No new messages"} 101 |
102 | { 103 | notifications.map((e, index) => { 104 | return ( 105 |
{ 106 | dispatch(setActiveChat(e.chatId)) 107 | dispatch(setNotifications(notifications.filter((data) => data !== e))) 108 | 109 | }} key={index} className='text-[12.5px] text-black px-2 cursor-pointer' > 110 | 111 | {e.chatId.isGroup ? `New Message in ${e.chatId.chatName}` : `New Message from ${getSender(activeUser, e.chatId.users)}`} 112 |
113 | 114 | ) 115 | 116 | }) 117 | } 118 |
119 | 123 |
124 |
125 | 126 |
127 | 128 |
129 |
e.preventDefault()}> 130 | 131 | 132 | 133 |
134 | 135 |
136 | 137 |
138 | 139 | 140 |
141 | 142 | 143 |
144 |
145 | 146 | 147 | 148 | 149 | 150 |
151 | 152 | 153 |
: 154 | } 155 | 156 | 157 | 158 | 159 | 160 |
161 |
162 | 163 | 164 | ) 165 | } 166 | 167 | export default Home -------------------------------------------------------------------------------- /clients/src/components/Model.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import Box from '@mui/material/Box'; 3 | import Modal from '@mui/material/Modal'; 4 | import { useDispatch, useSelector } from 'react-redux'; 5 | import { RxCross2 } from "react-icons/rx" 6 | import { useEffect } from 'react'; 7 | import { searchUsers } from '../apis/auth'; 8 | import { addToGroup, removeUser, renameGroup } from '../apis/chat'; 9 | import { fetchChats } from '../redux/chatsSlice'; 10 | import Search from './group/Search'; 11 | import { getChatName, getChatPhoto } from '../utils/logics'; 12 | const style = { 13 | position: 'absolute', 14 | top: '50%', 15 | left: '50%', 16 | transform: 'translate(-50%, -50%)', 17 | width: "fit-content", 18 | bgcolor: 'background.paper', 19 | boxShadow: 24, 20 | p: 2, 21 | }; 22 | function Model(props) { 23 | const [open, setOpen] = useState(false); 24 | const dispatch = useDispatch() 25 | const [searchResults, setSearchResults] = useState([]) 26 | const [name, setName] = useState("") 27 | const [search, setSearch] = useState("") 28 | const [isLoading, setIsLoading] = useState(false) 29 | const [members, setMembers] = useState([]) 30 | const { activeChat } = useSelector((state) => state.chats) 31 | const activeUser = useSelector((state) => state.activeUser) 32 | 33 | const handleOpen = () => { 34 | setOpen(true); 35 | setName(getChatName(activeChat, activeUser)) 36 | }; 37 | const handleClose = () => { 38 | setOpen(false); 39 | setSearch("") 40 | setSearchResults([]) 41 | }; 42 | const handleClick = async (e) => { 43 | if (members.includes(e)) { 44 | return 45 | } 46 | await addToGroup({ userId: e?._id, chatId: activeChat?._id }) 47 | setMembers([...members, e]) 48 | 49 | } 50 | 51 | const updateBtn = async () => { 52 | if (name) { 53 | let data = await renameGroup({ chatId: activeChat._id, chatName: name }) 54 | if (data) { 55 | dispatch(fetchChats()) 56 | setOpen(false) 57 | } 58 | } 59 | setOpen(false) 60 | } 61 | const deleteSelected = async (ele) => { 62 | const res = await removeUser({ chatId: activeChat._id, userId: ele._id }) 63 | if (res._id) { 64 | setMembers(members.filter((e) => e._id !== ele._id)) 65 | 66 | dispatch(fetchChats()) 67 | setOpen(false) 68 | 69 | } 70 | return 71 | } 72 | const leaveGroup = async () => { 73 | const res = await removeUser({ chatId: activeChat._id, userId: activeUser.id }) 74 | if (res._id) { 75 | dispatch(fetchChats()) 76 | setOpen(false) 77 | } 78 | return 79 | } 80 | useEffect(() => { 81 | setMembers(activeChat?.users.map((e) => e)) 82 | }, [activeChat]) 83 | useEffect(() => { 84 | const searchChange = async () => { 85 | setIsLoading(true) 86 | const { data } = await searchUsers(search) 87 | setSearchResults(data) 88 | setIsLoading(false) 89 | } 90 | searchChange() 91 | }, [search]) 92 | return ( 93 | <> 94 | 95 | 96 | 97 | 98 | 102 | { 103 | activeChat?.isGroup ? 104 | 105 | 111 | 112 |
{getChatName(activeChat, activeUser)}
113 |
114 |
Members
115 |
116 | { 117 | members.length > 0 && members?.map((e) => { 118 | return ( 119 | 123 | ) 124 | }) 125 | } 126 |
127 |
128 |
e.preventDefault()}> 129 | setName(e.target.value)} value={name} className="border-[#c4ccd5] border-[1px] text-[13.5px] py-[4px] px-2 w-[100%]" type="text" name="chatName" placeholder="Group Name" required /> 130 | setSearch(e.target.value)} className="border-[#c4ccd5] border-[1px] text-[13.5px] py-[4px] px-2 w-[100%]" type="text" name="users" placeholder="add users" /> 131 |
132 | {/*
*/} 133 | 134 | 135 | 136 |
137 | 138 | 139 | 140 |
141 |
142 |
143 | 144 | : 150 | 151 |
152 | 153 |

{getChatName(activeChat, activeUser)}

154 | 155 |

{!activeChat?.isGroup && activeChat?.users[0]?._id === activeUser.id ? activeChat?.users[1]?.email : activeChat?.users[0]?.email}

156 |
157 | 158 |
{!activeChat?.isGroup && activeChat?.users[0]?._id === activeUser.id ? activeChat?.users[1]?.bio : activeChat?.users[0]?.bio}
159 |
160 |
161 | 162 | 163 |
164 |
165 | } 166 | 167 | 168 | 169 | 170 | 171 | ) 172 | } 173 | 174 | export default Model -------------------------------------------------------------------------------- /clients/src/pages/Chat.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import Model from '../components/Model'; 4 | import { BsEmojiSmile, BsFillEmojiSmileFill } from "react-icons/bs" 5 | import { fetchMessages, sendMessage } from '../apis/messages'; 6 | import { useEffect } from 'react'; 7 | import MessageHistory from '../components/MessageHistory'; 8 | import io from "socket.io-client" 9 | import "./home.css" 10 | import { fetchChats, setNotifications } from '../redux/chatsSlice'; 11 | import Loading from '../components/ui/Loading'; 12 | import data from '@emoji-mart/data' 13 | import Picker from '@emoji-mart/react' 14 | import { getChatName } from '../utils/logics'; 15 | import Typing from '../components/ui/Typing'; 16 | import { validUser } from '../apis/auth'; 17 | const ENDPOINT = process.env.REACT_APP_SERVER_URL 18 | let socket, selectedChatCompare; 19 | 20 | function Chat(props) { 21 | const { activeChat, notifications } = useSelector((state) => state.chats) 22 | const dispatch = useDispatch() 23 | const [message, setMessage] = useState("") 24 | const [messages, setMessages] = useState([]) 25 | const [socketConnected, setSocketConnected] = useState(false) 26 | const [typing, setTyping] = useState(false) 27 | const [isTyping, setIsTyping] = useState(false) 28 | const [loading, setLoading] = useState(false) 29 | const [showPicker, setShowPicker] = useState(false); 30 | const activeUser = useSelector((state) => state.activeUser) 31 | 32 | const keyDownFunction = async (e) => { 33 | if ((e.key === "Enter" || e.type === "click") && (message)) { 34 | setMessage("") 35 | socket.emit("stop typing", activeChat._id) 36 | const data = await sendMessage({ chatId: activeChat._id, message }) 37 | socket.emit("new message", data) 38 | setMessages([...messages, data]) 39 | dispatch(fetchChats()) 40 | } 41 | } 42 | 43 | 44 | useEffect(() => { 45 | socket = io(ENDPOINT) 46 | socket.on("typing", () => setIsTyping(true)) 47 | socket.on("stop typing", () => setIsTyping(false)) 48 | }, []) 49 | 50 | useEffect(() => { 51 | socket.emit("setup", activeUser) 52 | socket.on("connected", () => { 53 | setSocketConnected(true) 54 | }) 55 | }, [messages, activeUser]) 56 | useEffect(() => { 57 | const fetchMessagesFunc = async () => { 58 | if (activeChat) { 59 | setLoading(true) 60 | const data = await fetchMessages(activeChat._id) 61 | setMessages(data) 62 | socket.emit("join room", activeChat._id) 63 | setLoading(false) 64 | 65 | } 66 | return 67 | } 68 | fetchMessagesFunc() 69 | selectedChatCompare = activeChat 70 | 71 | }, [activeChat]) 72 | useEffect(() => { 73 | socket.on("message recieved", (newMessageRecieved) => { 74 | if ((!selectedChatCompare || selectedChatCompare._id) !== newMessageRecieved.chatId._id) { 75 | if (!notifications.includes(newMessageRecieved)) { 76 | dispatch(setNotifications([newMessageRecieved, ...notifications])) 77 | } 78 | } 79 | else { 80 | setMessages([...messages, newMessageRecieved]) 81 | } 82 | dispatch(fetchChats()) 83 | }) 84 | }) 85 | useEffect(() => { 86 | const isValid = async () => { 87 | const data = await validUser() 88 | if (!data?.user) { 89 | window.location.href = "/login" 90 | } 91 | 92 | } 93 | isValid() 94 | }, []) 95 | if (loading) { 96 | return
97 | 98 |
99 | } 100 | return ( 101 | <> 102 | { 103 | activeChat ? 104 |
105 |
106 |
107 |
108 |
{getChatName(activeChat, activeUser)}
109 | {/*

Last seen 5 min ago

*/} 110 |
111 |
112 |
113 | 114 |
115 |
116 |
117 | 118 |
119 | { 120 | isTyping ? 121 | : "" 122 | } 123 | 124 |
125 |
126 |
127 | { 128 | showPicker && setMessage(message + e.native)} /> 129 | } 130 |
131 | 132 |
keyDownFunction(e)} onSubmit={(e) => e.preventDefault()}> 133 | { 134 | setMessage(e.target.value) 135 | if (!socketConnected) return 136 | if (!typing) { 137 | setTyping(true) 138 | socket.emit('typing', activeChat._id) 139 | } 140 | let lastTime = new Date().getTime() 141 | var time = 3000 142 | setTimeout(() => { 143 | var timeNow = new Date().getTime() 144 | var timeDiff = timeNow - lastTime 145 | if (timeDiff >= time && typing) { 146 | socket.emit("stop typing", activeChat._id) 147 | setTyping(false) 148 | } 149 | }, time) 150 | }} className='focus:outline-0 w-[100%] bg-[#f8f9fa]' type="text" name="message" placeholder="Enter message" value={message} /> 151 |
152 | 153 |
154 | 155 |
156 | {/* { 157 | isTyping ?
Loading
: "" 158 | } */} 159 |
160 | 161 |
setShowPicker(!showPicker)}> 162 | 163 | {showPicker ? : } 164 |
165 | 166 |
167 |
168 |
169 |
: 170 |
171 |
172 |
173 | User profile 174 |

Welcome {activeUser.name}

175 |
176 |
177 |
178 | 179 | } 180 | 181 | ) 182 | } 183 | 184 | export default Chat -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@aws-crypto/ie11-detection": { 8 | "version": "2.0.2", 9 | "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.2.tgz", 10 | "integrity": "sha512-5XDMQY98gMAf/WRTic5G++jfmS/VLM0rwpiOpaainKi4L0nqWMSB1SzsrEG5rjFZGYN6ZAefO+/Yta2dFM0kMw==", 11 | "optional": true, 12 | "requires": { 13 | "tslib": "^1.11.1" 14 | }, 15 | "dependencies": { 16 | "tslib": { 17 | "version": "1.14.1", 18 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 19 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 20 | "optional": true 21 | } 22 | } 23 | }, 24 | "@aws-crypto/sha256-browser": { 25 | "version": "2.0.0", 26 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", 27 | "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", 28 | "optional": true, 29 | "requires": { 30 | "@aws-crypto/ie11-detection": "^2.0.0", 31 | "@aws-crypto/sha256-js": "^2.0.0", 32 | "@aws-crypto/supports-web-crypto": "^2.0.0", 33 | "@aws-crypto/util": "^2.0.0", 34 | "@aws-sdk/types": "^3.1.0", 35 | "@aws-sdk/util-locate-window": "^3.0.0", 36 | "@aws-sdk/util-utf8-browser": "^3.0.0", 37 | "tslib": "^1.11.1" 38 | }, 39 | "dependencies": { 40 | "tslib": { 41 | "version": "1.14.1", 42 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 43 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 44 | "optional": true 45 | } 46 | } 47 | }, 48 | "@aws-crypto/sha256-js": { 49 | "version": "2.0.0", 50 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", 51 | "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", 52 | "optional": true, 53 | "requires": { 54 | "@aws-crypto/util": "^2.0.0", 55 | "@aws-sdk/types": "^3.1.0", 56 | "tslib": "^1.11.1" 57 | }, 58 | "dependencies": { 59 | "tslib": { 60 | "version": "1.14.1", 61 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 62 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 63 | "optional": true 64 | } 65 | } 66 | }, 67 | "@aws-crypto/supports-web-crypto": { 68 | "version": "2.0.2", 69 | "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.2.tgz", 70 | "integrity": "sha512-6mbSsLHwZ99CTOOswvCRP3C+VCWnzBf+1SnbWxzzJ9lR0mA0JnY2JEAhp8rqmTE0GPFy88rrM27ffgp62oErMQ==", 71 | "optional": true, 72 | "requires": { 73 | "tslib": "^1.11.1" 74 | }, 75 | "dependencies": { 76 | "tslib": { 77 | "version": "1.14.1", 78 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 79 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 80 | "optional": true 81 | } 82 | } 83 | }, 84 | "@aws-crypto/util": { 85 | "version": "2.0.2", 86 | "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.2.tgz", 87 | "integrity": "sha512-Lgu5v/0e/BcrZ5m/IWqzPUf3UYFTy/PpeED+uc9SWUR1iZQL8XXbGQg10UfllwwBryO3hFF5dizK+78aoXC1eA==", 88 | "optional": true, 89 | "requires": { 90 | "@aws-sdk/types": "^3.110.0", 91 | "@aws-sdk/util-utf8-browser": "^3.0.0", 92 | "tslib": "^1.11.1" 93 | }, 94 | "dependencies": { 95 | "tslib": { 96 | "version": "1.14.1", 97 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 98 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 99 | "optional": true 100 | } 101 | } 102 | }, 103 | "@aws-sdk/abort-controller": { 104 | "version": "3.226.0", 105 | "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.226.0.tgz", 106 | "integrity": "sha512-cJVzr1xxPBd08voknXvR0RLgtZKGKt6WyDpH/BaPCu3rfSqWCDZKzwqe940eqosjmKrxC6pUZNKASIqHOQ8xxQ==", 107 | "optional": true, 108 | "requires": { 109 | "@aws-sdk/types": "3.226.0", 110 | "tslib": "^2.3.1" 111 | } 112 | }, 113 | "@aws-sdk/client-cognito-identity": { 114 | "version": "3.241.0", 115 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.241.0.tgz", 116 | "integrity": "sha512-9X/MwcnSwWfB0ijggFjyBWa4gtlUAyI39eBaVSE0AxMcgLlHKedEK6w5F1RrtvWqb7KyJDsyAysVecU4E9zQQQ==", 117 | "optional": true, 118 | "requires": { 119 | "@aws-crypto/sha256-browser": "2.0.0", 120 | "@aws-crypto/sha256-js": "2.0.0", 121 | "@aws-sdk/client-sts": "3.241.0", 122 | "@aws-sdk/config-resolver": "3.234.0", 123 | "@aws-sdk/credential-provider-node": "3.241.0", 124 | "@aws-sdk/fetch-http-handler": "3.226.0", 125 | "@aws-sdk/hash-node": "3.226.0", 126 | "@aws-sdk/invalid-dependency": "3.226.0", 127 | "@aws-sdk/middleware-content-length": "3.226.0", 128 | "@aws-sdk/middleware-endpoint": "3.226.0", 129 | "@aws-sdk/middleware-host-header": "3.226.0", 130 | "@aws-sdk/middleware-logger": "3.226.0", 131 | "@aws-sdk/middleware-recursion-detection": "3.226.0", 132 | "@aws-sdk/middleware-retry": "3.235.0", 133 | "@aws-sdk/middleware-serde": "3.226.0", 134 | "@aws-sdk/middleware-signing": "3.226.0", 135 | "@aws-sdk/middleware-stack": "3.226.0", 136 | "@aws-sdk/middleware-user-agent": "3.226.0", 137 | "@aws-sdk/node-config-provider": "3.226.0", 138 | "@aws-sdk/node-http-handler": "3.226.0", 139 | "@aws-sdk/protocol-http": "3.226.0", 140 | "@aws-sdk/smithy-client": "3.234.0", 141 | "@aws-sdk/types": "3.226.0", 142 | "@aws-sdk/url-parser": "3.226.0", 143 | "@aws-sdk/util-base64": "3.208.0", 144 | "@aws-sdk/util-body-length-browser": "3.188.0", 145 | "@aws-sdk/util-body-length-node": "3.208.0", 146 | "@aws-sdk/util-defaults-mode-browser": "3.234.0", 147 | "@aws-sdk/util-defaults-mode-node": "3.234.0", 148 | "@aws-sdk/util-endpoints": "3.241.0", 149 | "@aws-sdk/util-retry": "3.229.0", 150 | "@aws-sdk/util-user-agent-browser": "3.226.0", 151 | "@aws-sdk/util-user-agent-node": "3.226.0", 152 | "@aws-sdk/util-utf8-browser": "3.188.0", 153 | "@aws-sdk/util-utf8-node": "3.208.0", 154 | "tslib": "^2.3.1" 155 | } 156 | }, 157 | "@aws-sdk/client-sso": { 158 | "version": "3.241.0", 159 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.241.0.tgz", 160 | "integrity": "sha512-Jm4HR+RYAqKMEYZvvWaq0NYUKKonyInOeubObXH4BLXZpmUBSdYCSjjLdNJY3jkQoxbDVPVMIurVNh5zT5SMRw==", 161 | "optional": true, 162 | "requires": { 163 | "@aws-crypto/sha256-browser": "2.0.0", 164 | "@aws-crypto/sha256-js": "2.0.0", 165 | "@aws-sdk/config-resolver": "3.234.0", 166 | "@aws-sdk/fetch-http-handler": "3.226.0", 167 | "@aws-sdk/hash-node": "3.226.0", 168 | "@aws-sdk/invalid-dependency": "3.226.0", 169 | "@aws-sdk/middleware-content-length": "3.226.0", 170 | "@aws-sdk/middleware-endpoint": "3.226.0", 171 | "@aws-sdk/middleware-host-header": "3.226.0", 172 | "@aws-sdk/middleware-logger": "3.226.0", 173 | "@aws-sdk/middleware-recursion-detection": "3.226.0", 174 | "@aws-sdk/middleware-retry": "3.235.0", 175 | "@aws-sdk/middleware-serde": "3.226.0", 176 | "@aws-sdk/middleware-stack": "3.226.0", 177 | "@aws-sdk/middleware-user-agent": "3.226.0", 178 | "@aws-sdk/node-config-provider": "3.226.0", 179 | "@aws-sdk/node-http-handler": "3.226.0", 180 | "@aws-sdk/protocol-http": "3.226.0", 181 | "@aws-sdk/smithy-client": "3.234.0", 182 | "@aws-sdk/types": "3.226.0", 183 | "@aws-sdk/url-parser": "3.226.0", 184 | "@aws-sdk/util-base64": "3.208.0", 185 | "@aws-sdk/util-body-length-browser": "3.188.0", 186 | "@aws-sdk/util-body-length-node": "3.208.0", 187 | "@aws-sdk/util-defaults-mode-browser": "3.234.0", 188 | "@aws-sdk/util-defaults-mode-node": "3.234.0", 189 | "@aws-sdk/util-endpoints": "3.241.0", 190 | "@aws-sdk/util-retry": "3.229.0", 191 | "@aws-sdk/util-user-agent-browser": "3.226.0", 192 | "@aws-sdk/util-user-agent-node": "3.226.0", 193 | "@aws-sdk/util-utf8-browser": "3.188.0", 194 | "@aws-sdk/util-utf8-node": "3.208.0", 195 | "tslib": "^2.3.1" 196 | } 197 | }, 198 | "@aws-sdk/client-sso-oidc": { 199 | "version": "3.241.0", 200 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.241.0.tgz", 201 | "integrity": "sha512-/Ml2QBGpGfUEeBrPzBZhSTBkHuXFD2EAZEIHGCBH4tKaURDI6/FoGI8P1Rl4BzoFt+II/Cr91Eox6YT9EwChsQ==", 202 | "optional": true, 203 | "requires": { 204 | "@aws-crypto/sha256-browser": "2.0.0", 205 | "@aws-crypto/sha256-js": "2.0.0", 206 | "@aws-sdk/config-resolver": "3.234.0", 207 | "@aws-sdk/fetch-http-handler": "3.226.0", 208 | "@aws-sdk/hash-node": "3.226.0", 209 | "@aws-sdk/invalid-dependency": "3.226.0", 210 | "@aws-sdk/middleware-content-length": "3.226.0", 211 | "@aws-sdk/middleware-endpoint": "3.226.0", 212 | "@aws-sdk/middleware-host-header": "3.226.0", 213 | "@aws-sdk/middleware-logger": "3.226.0", 214 | "@aws-sdk/middleware-recursion-detection": "3.226.0", 215 | "@aws-sdk/middleware-retry": "3.235.0", 216 | "@aws-sdk/middleware-serde": "3.226.0", 217 | "@aws-sdk/middleware-stack": "3.226.0", 218 | "@aws-sdk/middleware-user-agent": "3.226.0", 219 | "@aws-sdk/node-config-provider": "3.226.0", 220 | "@aws-sdk/node-http-handler": "3.226.0", 221 | "@aws-sdk/protocol-http": "3.226.0", 222 | "@aws-sdk/smithy-client": "3.234.0", 223 | "@aws-sdk/types": "3.226.0", 224 | "@aws-sdk/url-parser": "3.226.0", 225 | "@aws-sdk/util-base64": "3.208.0", 226 | "@aws-sdk/util-body-length-browser": "3.188.0", 227 | "@aws-sdk/util-body-length-node": "3.208.0", 228 | "@aws-sdk/util-defaults-mode-browser": "3.234.0", 229 | "@aws-sdk/util-defaults-mode-node": "3.234.0", 230 | "@aws-sdk/util-endpoints": "3.241.0", 231 | "@aws-sdk/util-retry": "3.229.0", 232 | "@aws-sdk/util-user-agent-browser": "3.226.0", 233 | "@aws-sdk/util-user-agent-node": "3.226.0", 234 | "@aws-sdk/util-utf8-browser": "3.188.0", 235 | "@aws-sdk/util-utf8-node": "3.208.0", 236 | "tslib": "^2.3.1" 237 | } 238 | }, 239 | "@aws-sdk/client-sts": { 240 | "version": "3.241.0", 241 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.241.0.tgz", 242 | "integrity": "sha512-vmlG8cJzRf8skCtTJbA2wBvD2c3NQ5gZryzJvTKDS06KzBzcEpnjlLseuTekcnOiRNekbFUX5hRu5Zj3N2ReLg==", 243 | "optional": true, 244 | "requires": { 245 | "@aws-crypto/sha256-browser": "2.0.0", 246 | "@aws-crypto/sha256-js": "2.0.0", 247 | "@aws-sdk/config-resolver": "3.234.0", 248 | "@aws-sdk/credential-provider-node": "3.241.0", 249 | "@aws-sdk/fetch-http-handler": "3.226.0", 250 | "@aws-sdk/hash-node": "3.226.0", 251 | "@aws-sdk/invalid-dependency": "3.226.0", 252 | "@aws-sdk/middleware-content-length": "3.226.0", 253 | "@aws-sdk/middleware-endpoint": "3.226.0", 254 | "@aws-sdk/middleware-host-header": "3.226.0", 255 | "@aws-sdk/middleware-logger": "3.226.0", 256 | "@aws-sdk/middleware-recursion-detection": "3.226.0", 257 | "@aws-sdk/middleware-retry": "3.235.0", 258 | "@aws-sdk/middleware-sdk-sts": "3.226.0", 259 | "@aws-sdk/middleware-serde": "3.226.0", 260 | "@aws-sdk/middleware-signing": "3.226.0", 261 | "@aws-sdk/middleware-stack": "3.226.0", 262 | "@aws-sdk/middleware-user-agent": "3.226.0", 263 | "@aws-sdk/node-config-provider": "3.226.0", 264 | "@aws-sdk/node-http-handler": "3.226.0", 265 | "@aws-sdk/protocol-http": "3.226.0", 266 | "@aws-sdk/smithy-client": "3.234.0", 267 | "@aws-sdk/types": "3.226.0", 268 | "@aws-sdk/url-parser": "3.226.0", 269 | "@aws-sdk/util-base64": "3.208.0", 270 | "@aws-sdk/util-body-length-browser": "3.188.0", 271 | "@aws-sdk/util-body-length-node": "3.208.0", 272 | "@aws-sdk/util-defaults-mode-browser": "3.234.0", 273 | "@aws-sdk/util-defaults-mode-node": "3.234.0", 274 | "@aws-sdk/util-endpoints": "3.241.0", 275 | "@aws-sdk/util-retry": "3.229.0", 276 | "@aws-sdk/util-user-agent-browser": "3.226.0", 277 | "@aws-sdk/util-user-agent-node": "3.226.0", 278 | "@aws-sdk/util-utf8-browser": "3.188.0", 279 | "@aws-sdk/util-utf8-node": "3.208.0", 280 | "fast-xml-parser": "4.0.11", 281 | "tslib": "^2.3.1" 282 | } 283 | }, 284 | "@aws-sdk/config-resolver": { 285 | "version": "3.234.0", 286 | "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", 287 | "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", 288 | "optional": true, 289 | "requires": { 290 | "@aws-sdk/signature-v4": "3.226.0", 291 | "@aws-sdk/types": "3.226.0", 292 | "@aws-sdk/util-config-provider": "3.208.0", 293 | "@aws-sdk/util-middleware": "3.226.0", 294 | "tslib": "^2.3.1" 295 | } 296 | }, 297 | "@aws-sdk/credential-provider-cognito-identity": { 298 | "version": "3.241.0", 299 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.241.0.tgz", 300 | "integrity": "sha512-e2hlXWG9DH93uVe2wHIUrUOrgZTLzCV3gBd10D3/usSzS4FvVVU7OmidnRPYCLLnt3EvnL5b4REOedO1q8hv8g==", 301 | "optional": true, 302 | "requires": { 303 | "@aws-sdk/client-cognito-identity": "3.241.0", 304 | "@aws-sdk/property-provider": "3.226.0", 305 | "@aws-sdk/types": "3.226.0", 306 | "tslib": "^2.3.1" 307 | } 308 | }, 309 | "@aws-sdk/credential-provider-env": { 310 | "version": "3.226.0", 311 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.226.0.tgz", 312 | "integrity": "sha512-sd8uK1ojbXxaZXlthzw/VXZwCPUtU3PjObOfr3Evj7MPIM2IH8h29foOlggx939MdLQGboJf9gKvLlvKDWtJRA==", 313 | "optional": true, 314 | "requires": { 315 | "@aws-sdk/property-provider": "3.226.0", 316 | "@aws-sdk/types": "3.226.0", 317 | "tslib": "^2.3.1" 318 | } 319 | }, 320 | "@aws-sdk/credential-provider-imds": { 321 | "version": "3.226.0", 322 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.226.0.tgz", 323 | "integrity": "sha512-//z/COQm2AjYFI1Lb0wKHTQSrvLFTyuKLFQGPJsKS7DPoxGOCKB7hmYerlbl01IDoCxTdyL//TyyPxbZEOQD5Q==", 324 | "optional": true, 325 | "requires": { 326 | "@aws-sdk/node-config-provider": "3.226.0", 327 | "@aws-sdk/property-provider": "3.226.0", 328 | "@aws-sdk/types": "3.226.0", 329 | "@aws-sdk/url-parser": "3.226.0", 330 | "tslib": "^2.3.1" 331 | } 332 | }, 333 | "@aws-sdk/credential-provider-ini": { 334 | "version": "3.241.0", 335 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.241.0.tgz", 336 | "integrity": "sha512-CI+mu6h74Kzmscw35TvNkc/wYHsHPGAwP7humSHoWw53H9mVw21Ggft/dT1iFQQZWQ8BNXkzuXlNo1IlqwMgOA==", 337 | "optional": true, 338 | "requires": { 339 | "@aws-sdk/credential-provider-env": "3.226.0", 340 | "@aws-sdk/credential-provider-imds": "3.226.0", 341 | "@aws-sdk/credential-provider-process": "3.226.0", 342 | "@aws-sdk/credential-provider-sso": "3.241.0", 343 | "@aws-sdk/credential-provider-web-identity": "3.226.0", 344 | "@aws-sdk/property-provider": "3.226.0", 345 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 346 | "@aws-sdk/types": "3.226.0", 347 | "tslib": "^2.3.1" 348 | } 349 | }, 350 | "@aws-sdk/credential-provider-node": { 351 | "version": "3.241.0", 352 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.241.0.tgz", 353 | "integrity": "sha512-08zPQcD5o9brQmzEipWHeHgU85aQcEF8MWLfpeyjO6e1/l7ysQ35NsS+PYtv77nLpGCx/X+ZuW/KXWoRrbw77w==", 354 | "optional": true, 355 | "requires": { 356 | "@aws-sdk/credential-provider-env": "3.226.0", 357 | "@aws-sdk/credential-provider-imds": "3.226.0", 358 | "@aws-sdk/credential-provider-ini": "3.241.0", 359 | "@aws-sdk/credential-provider-process": "3.226.0", 360 | "@aws-sdk/credential-provider-sso": "3.241.0", 361 | "@aws-sdk/credential-provider-web-identity": "3.226.0", 362 | "@aws-sdk/property-provider": "3.226.0", 363 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 364 | "@aws-sdk/types": "3.226.0", 365 | "tslib": "^2.3.1" 366 | } 367 | }, 368 | "@aws-sdk/credential-provider-process": { 369 | "version": "3.226.0", 370 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.226.0.tgz", 371 | "integrity": "sha512-iUDMdnrTvbvaCFhWwqyXrhvQ9+ojPqPqXhwZtY1X/Qaz+73S9gXBPJHZaZb2Ke0yKE1Ql3bJbKvmmxC/qLQMng==", 372 | "optional": true, 373 | "requires": { 374 | "@aws-sdk/property-provider": "3.226.0", 375 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 376 | "@aws-sdk/types": "3.226.0", 377 | "tslib": "^2.3.1" 378 | } 379 | }, 380 | "@aws-sdk/credential-provider-sso": { 381 | "version": "3.241.0", 382 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.241.0.tgz", 383 | "integrity": "sha512-6Bjd6eEIrVomRTrPrM4dlxusQm+KMJ9hLYKECCpFkwDKIK+pTgZNLRtQdalHyzwneHJPdimrm8cOv1kUQ8hPoA==", 384 | "optional": true, 385 | "requires": { 386 | "@aws-sdk/client-sso": "3.241.0", 387 | "@aws-sdk/property-provider": "3.226.0", 388 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 389 | "@aws-sdk/token-providers": "3.241.0", 390 | "@aws-sdk/types": "3.226.0", 391 | "tslib": "^2.3.1" 392 | } 393 | }, 394 | "@aws-sdk/credential-provider-web-identity": { 395 | "version": "3.226.0", 396 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.226.0.tgz", 397 | "integrity": "sha512-CCpv847rLB0SFOHz2igvUMFAzeT2fD3YnY4C8jltuJoEkn0ITn1Hlgt13nTJ5BUuvyti2mvyXZHmNzhMIMrIlw==", 398 | "optional": true, 399 | "requires": { 400 | "@aws-sdk/property-provider": "3.226.0", 401 | "@aws-sdk/types": "3.226.0", 402 | "tslib": "^2.3.1" 403 | } 404 | }, 405 | "@aws-sdk/credential-providers": { 406 | "version": "3.241.0", 407 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.241.0.tgz", 408 | "integrity": "sha512-J3Q45t1o35OhUI6gWks7rmosPT+mFWXiaHl2LST509Ovjwx6SFs2PvbGP6n7xqUzxyq5Rk6FzZBwB8ItuAa6Qw==", 409 | "optional": true, 410 | "requires": { 411 | "@aws-sdk/client-cognito-identity": "3.241.0", 412 | "@aws-sdk/client-sso": "3.241.0", 413 | "@aws-sdk/client-sts": "3.241.0", 414 | "@aws-sdk/credential-provider-cognito-identity": "3.241.0", 415 | "@aws-sdk/credential-provider-env": "3.226.0", 416 | "@aws-sdk/credential-provider-imds": "3.226.0", 417 | "@aws-sdk/credential-provider-ini": "3.241.0", 418 | "@aws-sdk/credential-provider-node": "3.241.0", 419 | "@aws-sdk/credential-provider-process": "3.226.0", 420 | "@aws-sdk/credential-provider-sso": "3.241.0", 421 | "@aws-sdk/credential-provider-web-identity": "3.226.0", 422 | "@aws-sdk/property-provider": "3.226.0", 423 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 424 | "@aws-sdk/types": "3.226.0", 425 | "tslib": "^2.3.1" 426 | } 427 | }, 428 | "@aws-sdk/fetch-http-handler": { 429 | "version": "3.226.0", 430 | "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.226.0.tgz", 431 | "integrity": "sha512-JewZPMNEBXfi1xVnRa7pVtK/zgZD8/lQ/YnD8pq79WuMa2cwyhDtr8oqCoqsPW+WJT5ScXoMtuHxN78l8eKWgg==", 432 | "optional": true, 433 | "requires": { 434 | "@aws-sdk/protocol-http": "3.226.0", 435 | "@aws-sdk/querystring-builder": "3.226.0", 436 | "@aws-sdk/types": "3.226.0", 437 | "@aws-sdk/util-base64": "3.208.0", 438 | "tslib": "^2.3.1" 439 | } 440 | }, 441 | "@aws-sdk/hash-node": { 442 | "version": "3.226.0", 443 | "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.226.0.tgz", 444 | "integrity": "sha512-MdlJhJ9/Espwd0+gUXdZRsHuostB2WxEVAszWxobP0FTT9PnicqnfK7ExmW+DUAc0ywxtEbR3e0UND65rlSTVw==", 445 | "optional": true, 446 | "requires": { 447 | "@aws-sdk/types": "3.226.0", 448 | "@aws-sdk/util-buffer-from": "3.208.0", 449 | "tslib": "^2.3.1" 450 | } 451 | }, 452 | "@aws-sdk/invalid-dependency": { 453 | "version": "3.226.0", 454 | "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.226.0.tgz", 455 | "integrity": "sha512-QXOYFmap8g9QzRjumcRCIo2GEZkdCwd7ePQW0OABWPhKHzlJ74vvBxywjU3s39EEBEluWXtZ7Iufg6GxZM4ifw==", 456 | "optional": true, 457 | "requires": { 458 | "@aws-sdk/types": "3.226.0", 459 | "tslib": "^2.3.1" 460 | } 461 | }, 462 | "@aws-sdk/is-array-buffer": { 463 | "version": "3.201.0", 464 | "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.201.0.tgz", 465 | "integrity": "sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==", 466 | "optional": true, 467 | "requires": { 468 | "tslib": "^2.3.1" 469 | } 470 | }, 471 | "@aws-sdk/middleware-content-length": { 472 | "version": "3.226.0", 473 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.226.0.tgz", 474 | "integrity": "sha512-ksUzlHJN2JMuyavjA46a4sctvnrnITqt2tbGGWWrAuXY1mel2j+VbgnmJUiwHKUO6bTFBBeft5Vd1TSOb4JmiA==", 475 | "optional": true, 476 | "requires": { 477 | "@aws-sdk/protocol-http": "3.226.0", 478 | "@aws-sdk/types": "3.226.0", 479 | "tslib": "^2.3.1" 480 | } 481 | }, 482 | "@aws-sdk/middleware-endpoint": { 483 | "version": "3.226.0", 484 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.226.0.tgz", 485 | "integrity": "sha512-EvLFafjtUxTT0AC9p3aBQu1/fjhWdIeK58jIXaNFONfZ3F8QbEYUPuF/SqZvJM6cWfOO9qwYKkRDbCSTYhprIg==", 486 | "optional": true, 487 | "requires": { 488 | "@aws-sdk/middleware-serde": "3.226.0", 489 | "@aws-sdk/protocol-http": "3.226.0", 490 | "@aws-sdk/signature-v4": "3.226.0", 491 | "@aws-sdk/types": "3.226.0", 492 | "@aws-sdk/url-parser": "3.226.0", 493 | "@aws-sdk/util-config-provider": "3.208.0", 494 | "@aws-sdk/util-middleware": "3.226.0", 495 | "tslib": "^2.3.1" 496 | } 497 | }, 498 | "@aws-sdk/middleware-host-header": { 499 | "version": "3.226.0", 500 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.226.0.tgz", 501 | "integrity": "sha512-haVkWVh6BUPwKgWwkL6sDvTkcZWvJjv8AgC8jiQuSl8GLZdzHTB8Qhi3IsfFta9HAuoLjxheWBE5Z/L0UrfhLA==", 502 | "optional": true, 503 | "requires": { 504 | "@aws-sdk/protocol-http": "3.226.0", 505 | "@aws-sdk/types": "3.226.0", 506 | "tslib": "^2.3.1" 507 | } 508 | }, 509 | "@aws-sdk/middleware-logger": { 510 | "version": "3.226.0", 511 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.226.0.tgz", 512 | "integrity": "sha512-m9gtLrrYnpN6yckcQ09rV7ExWOLMuq8mMPF/K3DbL/YL0TuILu9i2T1W+JuxSX+K9FMG2HrLAKivE/kMLr55xA==", 513 | "optional": true, 514 | "requires": { 515 | "@aws-sdk/types": "3.226.0", 516 | "tslib": "^2.3.1" 517 | } 518 | }, 519 | "@aws-sdk/middleware-recursion-detection": { 520 | "version": "3.226.0", 521 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.226.0.tgz", 522 | "integrity": "sha512-mwRbdKEUeuNH5TEkyZ5FWxp6bL2UC1WbY+LDv6YjHxmSMKpAoOueEdtU34PqDOLrpXXxIGHDFmjeGeMfktyEcA==", 523 | "optional": true, 524 | "requires": { 525 | "@aws-sdk/protocol-http": "3.226.0", 526 | "@aws-sdk/types": "3.226.0", 527 | "tslib": "^2.3.1" 528 | } 529 | }, 530 | "@aws-sdk/middleware-retry": { 531 | "version": "3.235.0", 532 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.235.0.tgz", 533 | "integrity": "sha512-50WHbJGpD3SNp9763MAlHqIhXil++JdQbKejNpHg7HsJne/ao3ub+fDOfx//mMBjpzBV25BGd5UlfL6blrClSg==", 534 | "optional": true, 535 | "requires": { 536 | "@aws-sdk/protocol-http": "3.226.0", 537 | "@aws-sdk/service-error-classification": "3.229.0", 538 | "@aws-sdk/types": "3.226.0", 539 | "@aws-sdk/util-middleware": "3.226.0", 540 | "@aws-sdk/util-retry": "3.229.0", 541 | "tslib": "^2.3.1", 542 | "uuid": "^8.3.2" 543 | } 544 | }, 545 | "@aws-sdk/middleware-sdk-sts": { 546 | "version": "3.226.0", 547 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.226.0.tgz", 548 | "integrity": "sha512-NN9T/qoSD1kZvAT+VLny3NnlqgylYQcsgV3rvi/8lYzw/G/2s8VS6sm/VTWGGZhx08wZRv20MWzYu3bftcyqUg==", 549 | "optional": true, 550 | "requires": { 551 | "@aws-sdk/middleware-signing": "3.226.0", 552 | "@aws-sdk/property-provider": "3.226.0", 553 | "@aws-sdk/protocol-http": "3.226.0", 554 | "@aws-sdk/signature-v4": "3.226.0", 555 | "@aws-sdk/types": "3.226.0", 556 | "tslib": "^2.3.1" 557 | } 558 | }, 559 | "@aws-sdk/middleware-serde": { 560 | "version": "3.226.0", 561 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.226.0.tgz", 562 | "integrity": "sha512-nPuOOAkSfx9TxzdKFx0X2bDlinOxGrqD7iof926K/AEflxGD1DBdcaDdjlYlPDW2CVE8LV/rAgbYuLxh/E/1VA==", 563 | "optional": true, 564 | "requires": { 565 | "@aws-sdk/types": "3.226.0", 566 | "tslib": "^2.3.1" 567 | } 568 | }, 569 | "@aws-sdk/middleware-signing": { 570 | "version": "3.226.0", 571 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.226.0.tgz", 572 | "integrity": "sha512-E6HmtPcl+IjYDDzi1xI2HpCbBq2avNWcjvCriMZWuTAtRVpnA6XDDGW5GY85IfS3A8G8vuWqEVPr8JcYUcjfew==", 573 | "optional": true, 574 | "requires": { 575 | "@aws-sdk/property-provider": "3.226.0", 576 | "@aws-sdk/protocol-http": "3.226.0", 577 | "@aws-sdk/signature-v4": "3.226.0", 578 | "@aws-sdk/types": "3.226.0", 579 | "@aws-sdk/util-middleware": "3.226.0", 580 | "tslib": "^2.3.1" 581 | } 582 | }, 583 | "@aws-sdk/middleware-stack": { 584 | "version": "3.226.0", 585 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.226.0.tgz", 586 | "integrity": "sha512-85wF29LvPvpoed60fZGDYLwv1Zpd/cM0C22WSSFPw1SSJeqO4gtFYyCg2squfT3KI6kF43IIkOCJ+L7GtryPug==", 587 | "optional": true, 588 | "requires": { 589 | "tslib": "^2.3.1" 590 | } 591 | }, 592 | "@aws-sdk/middleware-user-agent": { 593 | "version": "3.226.0", 594 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.226.0.tgz", 595 | "integrity": "sha512-N1WnfzCW1Y5yWhVAphf8OPGTe8Df3vmV7/LdsoQfmpkCZgLZeK2o0xITkUQhRj1mbw7yp8tVFLFV3R2lMurdAQ==", 596 | "optional": true, 597 | "requires": { 598 | "@aws-sdk/protocol-http": "3.226.0", 599 | "@aws-sdk/types": "3.226.0", 600 | "tslib": "^2.3.1" 601 | } 602 | }, 603 | "@aws-sdk/node-config-provider": { 604 | "version": "3.226.0", 605 | "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.226.0.tgz", 606 | "integrity": "sha512-B8lQDqiRk7X5izFEUMXmi8CZLOKCTWQJU9HQf3ako+sF0gexo4nHN3jhoRWyLtcgC5S3on/2jxpAcqtm7kuY3w==", 607 | "optional": true, 608 | "requires": { 609 | "@aws-sdk/property-provider": "3.226.0", 610 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 611 | "@aws-sdk/types": "3.226.0", 612 | "tslib": "^2.3.1" 613 | } 614 | }, 615 | "@aws-sdk/node-http-handler": { 616 | "version": "3.226.0", 617 | "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.226.0.tgz", 618 | "integrity": "sha512-xQCddnZNMiPmjr3W7HYM+f5ir4VfxgJh37eqZwX6EZmyItFpNNeVzKUgA920ka1VPz/ZUYB+2OFGiX3LCLkkaA==", 619 | "optional": true, 620 | "requires": { 621 | "@aws-sdk/abort-controller": "3.226.0", 622 | "@aws-sdk/protocol-http": "3.226.0", 623 | "@aws-sdk/querystring-builder": "3.226.0", 624 | "@aws-sdk/types": "3.226.0", 625 | "tslib": "^2.3.1" 626 | } 627 | }, 628 | "@aws-sdk/property-provider": { 629 | "version": "3.226.0", 630 | "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.226.0.tgz", 631 | "integrity": "sha512-TsljjG+Sg0LmdgfiAlWohluWKnxB/k8xenjeozZfzOr5bHmNHtdbWv6BtNvD/R83hw7SFXxbJHlD5H4u9p2NFg==", 632 | "optional": true, 633 | "requires": { 634 | "@aws-sdk/types": "3.226.0", 635 | "tslib": "^2.3.1" 636 | } 637 | }, 638 | "@aws-sdk/protocol-http": { 639 | "version": "3.226.0", 640 | "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.226.0.tgz", 641 | "integrity": "sha512-zWkVqiTA9RXL6y0hhfZc9bcU4DX2NI6Hw9IhQmSPeM59mdbPjJlY4bLlMr5YxywqO3yQ/ylNoAfrEzrDjlOSRg==", 642 | "optional": true, 643 | "requires": { 644 | "@aws-sdk/types": "3.226.0", 645 | "tslib": "^2.3.1" 646 | } 647 | }, 648 | "@aws-sdk/querystring-builder": { 649 | "version": "3.226.0", 650 | "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.226.0.tgz", 651 | "integrity": "sha512-LVurypuNeotO4lmirKXRC4NYrZRAyMJXuwO0f2a5ZAUJCjauwYrifKue6yCfU7bls7gut7nfcR6B99WBYpHs3g==", 652 | "optional": true, 653 | "requires": { 654 | "@aws-sdk/types": "3.226.0", 655 | "@aws-sdk/util-uri-escape": "3.201.0", 656 | "tslib": "^2.3.1" 657 | } 658 | }, 659 | "@aws-sdk/querystring-parser": { 660 | "version": "3.226.0", 661 | "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.226.0.tgz", 662 | "integrity": "sha512-FzB+VrQ47KAFxiPt2YXrKZ8AOLZQqGTLCKHzx4bjxGmwgsjV8yIbtJiJhZLMcUQV4LtGeIY9ixIqQhGvnZHE4A==", 663 | "optional": true, 664 | "requires": { 665 | "@aws-sdk/types": "3.226.0", 666 | "tslib": "^2.3.1" 667 | } 668 | }, 669 | "@aws-sdk/service-error-classification": { 670 | "version": "3.229.0", 671 | "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.229.0.tgz", 672 | "integrity": "sha512-dnzWWQ0/NoWMUZ5C0DW3dPm0wC1O76Y/SpKbuJzWPkx1EYy6r8p32Ly4D9vUzrKDbRGf48YHIF2kOkBmu21CLg==", 673 | "optional": true 674 | }, 675 | "@aws-sdk/shared-ini-file-loader": { 676 | "version": "3.226.0", 677 | "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.226.0.tgz", 678 | "integrity": "sha512-661VQefsARxVyyV2FX9V61V+nNgImk7aN2hYlFKla6BCwZfMng+dEtD0xVGyg1PfRw0qvEv5LQyxMVgHcUSevA==", 679 | "optional": true, 680 | "requires": { 681 | "@aws-sdk/types": "3.226.0", 682 | "tslib": "^2.3.1" 683 | } 684 | }, 685 | "@aws-sdk/signature-v4": { 686 | "version": "3.226.0", 687 | "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.226.0.tgz", 688 | "integrity": "sha512-/R5q5agdPd7HJB68XMzpxrNPk158EHUvkFkuRu5Qf3kkkHebEzWEBlWoVpUe6ss4rP9Tqcue6xPuaftEmhjpYw==", 689 | "optional": true, 690 | "requires": { 691 | "@aws-sdk/is-array-buffer": "3.201.0", 692 | "@aws-sdk/types": "3.226.0", 693 | "@aws-sdk/util-hex-encoding": "3.201.0", 694 | "@aws-sdk/util-middleware": "3.226.0", 695 | "@aws-sdk/util-uri-escape": "3.201.0", 696 | "tslib": "^2.3.1" 697 | } 698 | }, 699 | "@aws-sdk/smithy-client": { 700 | "version": "3.234.0", 701 | "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", 702 | "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", 703 | "optional": true, 704 | "requires": { 705 | "@aws-sdk/middleware-stack": "3.226.0", 706 | "@aws-sdk/types": "3.226.0", 707 | "tslib": "^2.3.1" 708 | } 709 | }, 710 | "@aws-sdk/token-providers": { 711 | "version": "3.241.0", 712 | "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.241.0.tgz", 713 | "integrity": "sha512-79okvuOS7V559OIL/RalIPG98wzmWxeFOChFnbEjn2pKOyGQ6FJRwLPYZaVRtNdAtnkBNgRpmFq9dX843QxhtQ==", 714 | "optional": true, 715 | "requires": { 716 | "@aws-sdk/client-sso-oidc": "3.241.0", 717 | "@aws-sdk/property-provider": "3.226.0", 718 | "@aws-sdk/shared-ini-file-loader": "3.226.0", 719 | "@aws-sdk/types": "3.226.0", 720 | "tslib": "^2.3.1" 721 | } 722 | }, 723 | "@aws-sdk/types": { 724 | "version": "3.226.0", 725 | "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.226.0.tgz", 726 | "integrity": "sha512-MmmNHrWeO4man7wpOwrAhXlevqtOV9ZLcH4RhnG5LmRce0RFOApx24HoKENfFCcOyCm5LQBlsXCqi0dZWDWU0A==", 727 | "optional": true, 728 | "requires": { 729 | "tslib": "^2.3.1" 730 | } 731 | }, 732 | "@aws-sdk/url-parser": { 733 | "version": "3.226.0", 734 | "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.226.0.tgz", 735 | "integrity": "sha512-p5RLE0QWyP0OcTOLmFcLdVgUcUEzmEfmdrnOxyNzomcYb0p3vUagA5zfa1HVK2azsQJFBv28GfvMnba9bGhObg==", 736 | "optional": true, 737 | "requires": { 738 | "@aws-sdk/querystring-parser": "3.226.0", 739 | "@aws-sdk/types": "3.226.0", 740 | "tslib": "^2.3.1" 741 | } 742 | }, 743 | "@aws-sdk/util-base64": { 744 | "version": "3.208.0", 745 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.208.0.tgz", 746 | "integrity": "sha512-PQniZph5A6N7uuEOQi+1hnMz/FSOK/8kMFyFO+4DgA1dZ5pcKcn5wiFwHkcTb/BsgVqQa3Jx0VHNnvhlS8JyTg==", 747 | "optional": true, 748 | "requires": { 749 | "@aws-sdk/util-buffer-from": "3.208.0", 750 | "tslib": "^2.3.1" 751 | } 752 | }, 753 | "@aws-sdk/util-body-length-browser": { 754 | "version": "3.188.0", 755 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz", 756 | "integrity": "sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==", 757 | "optional": true, 758 | "requires": { 759 | "tslib": "^2.3.1" 760 | } 761 | }, 762 | "@aws-sdk/util-body-length-node": { 763 | "version": "3.208.0", 764 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.208.0.tgz", 765 | "integrity": "sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==", 766 | "optional": true, 767 | "requires": { 768 | "tslib": "^2.3.1" 769 | } 770 | }, 771 | "@aws-sdk/util-buffer-from": { 772 | "version": "3.208.0", 773 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.208.0.tgz", 774 | "integrity": "sha512-7L0XUixNEFcLUGPeBF35enCvB9Xl+K6SQsmbrPk1P3mlV9mguWSDQqbOBwY1Ir0OVbD6H/ZOQU7hI/9RtRI0Zw==", 775 | "optional": true, 776 | "requires": { 777 | "@aws-sdk/is-array-buffer": "3.201.0", 778 | "tslib": "^2.3.1" 779 | } 780 | }, 781 | "@aws-sdk/util-config-provider": { 782 | "version": "3.208.0", 783 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.208.0.tgz", 784 | "integrity": "sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==", 785 | "optional": true, 786 | "requires": { 787 | "tslib": "^2.3.1" 788 | } 789 | }, 790 | "@aws-sdk/util-defaults-mode-browser": { 791 | "version": "3.234.0", 792 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.234.0.tgz", 793 | "integrity": "sha512-IHMKXjTbOD8XMz5+2oCOsVP94BYb9YyjXdns0aAXr2NAo7k2+RCzXQ2DebJXppGda1F6opFutoKwyVSN0cmbMw==", 794 | "optional": true, 795 | "requires": { 796 | "@aws-sdk/property-provider": "3.226.0", 797 | "@aws-sdk/types": "3.226.0", 798 | "bowser": "^2.11.0", 799 | "tslib": "^2.3.1" 800 | } 801 | }, 802 | "@aws-sdk/util-defaults-mode-node": { 803 | "version": "3.234.0", 804 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.234.0.tgz", 805 | "integrity": "sha512-UGjQ+OjBYYhxFVtUY+jtr0ZZgzZh6OHtYwRhFt8IHewJXFCfZTyfsbX20szBj5y1S4HRIUJ7cwBLIytTqMbI5w==", 806 | "optional": true, 807 | "requires": { 808 | "@aws-sdk/config-resolver": "3.234.0", 809 | "@aws-sdk/credential-provider-imds": "3.226.0", 810 | "@aws-sdk/node-config-provider": "3.226.0", 811 | "@aws-sdk/property-provider": "3.226.0", 812 | "@aws-sdk/types": "3.226.0", 813 | "tslib": "^2.3.1" 814 | } 815 | }, 816 | "@aws-sdk/util-endpoints": { 817 | "version": "3.241.0", 818 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.241.0.tgz", 819 | "integrity": "sha512-jVf8bKrN22Ey0xLmj75sL7EUvm5HFpuOMkXsZkuXycKhCwLBcEUWlvtJYtRjOU1zScPQv9GMJd2QXQglp34iOQ==", 820 | "optional": true, 821 | "requires": { 822 | "@aws-sdk/types": "3.226.0", 823 | "tslib": "^2.3.1" 824 | } 825 | }, 826 | "@aws-sdk/util-hex-encoding": { 827 | "version": "3.201.0", 828 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.201.0.tgz", 829 | "integrity": "sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==", 830 | "optional": true, 831 | "requires": { 832 | "tslib": "^2.3.1" 833 | } 834 | }, 835 | "@aws-sdk/util-locate-window": { 836 | "version": "3.208.0", 837 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.208.0.tgz", 838 | "integrity": "sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==", 839 | "optional": true, 840 | "requires": { 841 | "tslib": "^2.3.1" 842 | } 843 | }, 844 | "@aws-sdk/util-middleware": { 845 | "version": "3.226.0", 846 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.226.0.tgz", 847 | "integrity": "sha512-B96CQnwX4gRvQdaQkdUtqvDPkrptV5+va6FVeJOocU/DbSYMAScLxtR3peMS8cnlOT6nL1Eoa42OI9AfZz1VwQ==", 848 | "optional": true, 849 | "requires": { 850 | "tslib": "^2.3.1" 851 | } 852 | }, 853 | "@aws-sdk/util-retry": { 854 | "version": "3.229.0", 855 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.229.0.tgz", 856 | "integrity": "sha512-0zKTqi0P1inD0LzIMuXRIYYQ/8c1lWMg/cfiqUcIAF1TpatlpZuN7umU0ierpBFud7S+zDgg0oemh+Nj8xliJw==", 857 | "optional": true, 858 | "requires": { 859 | "@aws-sdk/service-error-classification": "3.229.0", 860 | "tslib": "^2.3.1" 861 | } 862 | }, 863 | "@aws-sdk/util-uri-escape": { 864 | "version": "3.201.0", 865 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.201.0.tgz", 866 | "integrity": "sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==", 867 | "optional": true, 868 | "requires": { 869 | "tslib": "^2.3.1" 870 | } 871 | }, 872 | "@aws-sdk/util-user-agent-browser": { 873 | "version": "3.226.0", 874 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.226.0.tgz", 875 | "integrity": "sha512-PhBIu2h6sPJPcv2I7ELfFizdl5pNiL4LfxrasMCYXQkJvVnoXztHA1x+CQbXIdtZOIlpjC+6BjDcE0uhnpvfcA==", 876 | "optional": true, 877 | "requires": { 878 | "@aws-sdk/types": "3.226.0", 879 | "bowser": "^2.11.0", 880 | "tslib": "^2.3.1" 881 | } 882 | }, 883 | "@aws-sdk/util-user-agent-node": { 884 | "version": "3.226.0", 885 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.226.0.tgz", 886 | "integrity": "sha512-othPc5Dz/pkYkxH+nZPhc1Al0HndQT8zHD4e9h+EZ+8lkd8n+IsnLfTS/mSJWrfiC6UlNRVw55cItstmJyMe/A==", 887 | "optional": true, 888 | "requires": { 889 | "@aws-sdk/node-config-provider": "3.226.0", 890 | "@aws-sdk/types": "3.226.0", 891 | "tslib": "^2.3.1" 892 | } 893 | }, 894 | "@aws-sdk/util-utf8-browser": { 895 | "version": "3.188.0", 896 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.188.0.tgz", 897 | "integrity": "sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q==", 898 | "optional": true, 899 | "requires": { 900 | "tslib": "^2.3.1" 901 | } 902 | }, 903 | "@aws-sdk/util-utf8-node": { 904 | "version": "3.208.0", 905 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.208.0.tgz", 906 | "integrity": "sha512-jKY87Acv0yWBdFxx6bveagy5FYjz+dtV8IPT7ay1E2WPWH1czoIdMAkc8tSInK31T6CRnHWkLZ1qYwCbgRfERQ==", 907 | "optional": true, 908 | "requires": { 909 | "@aws-sdk/util-buffer-from": "3.208.0", 910 | "tslib": "^2.3.1" 911 | } 912 | }, 913 | "@socket.io/component-emitter": { 914 | "version": "3.1.0", 915 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", 916 | "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" 917 | }, 918 | "@types/cookie": { 919 | "version": "0.4.1", 920 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 921 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 922 | }, 923 | "@types/cors": { 924 | "version": "2.8.13", 925 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", 926 | "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", 927 | "requires": { 928 | "@types/node": "*" 929 | } 930 | }, 931 | "@types/node": { 932 | "version": "18.11.18", 933 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", 934 | "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" 935 | }, 936 | "@types/webidl-conversions": { 937 | "version": "7.0.0", 938 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 939 | "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" 940 | }, 941 | "@types/whatwg-url": { 942 | "version": "8.2.2", 943 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", 944 | "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", 945 | "requires": { 946 | "@types/node": "*", 947 | "@types/webidl-conversions": "*" 948 | } 949 | }, 950 | "accepts": { 951 | "version": "1.3.8", 952 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 953 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 954 | "requires": { 955 | "mime-types": "~2.1.34", 956 | "negotiator": "0.6.3" 957 | } 958 | }, 959 | "agent-base": { 960 | "version": "6.0.2", 961 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 962 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 963 | "requires": { 964 | "debug": "4" 965 | }, 966 | "dependencies": { 967 | "debug": { 968 | "version": "4.3.4", 969 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 970 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 971 | "requires": { 972 | "ms": "2.1.2" 973 | } 974 | }, 975 | "ms": { 976 | "version": "2.1.2", 977 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 978 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 979 | } 980 | } 981 | }, 982 | "array-flatten": { 983 | "version": "1.1.1", 984 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 985 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 986 | }, 987 | "arrify": { 988 | "version": "2.0.1", 989 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", 990 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" 991 | }, 992 | "base64-js": { 993 | "version": "1.5.1", 994 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 995 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 996 | }, 997 | "base64id": { 998 | "version": "2.0.0", 999 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 1000 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" 1001 | }, 1002 | "bcryptjs": { 1003 | "version": "2.4.3", 1004 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", 1005 | "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" 1006 | }, 1007 | "bignumber.js": { 1008 | "version": "9.1.1", 1009 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", 1010 | "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==" 1011 | }, 1012 | "body-parser": { 1013 | "version": "1.20.1", 1014 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 1015 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 1016 | "requires": { 1017 | "bytes": "3.1.2", 1018 | "content-type": "~1.0.4", 1019 | "debug": "2.6.9", 1020 | "depd": "2.0.0", 1021 | "destroy": "1.2.0", 1022 | "http-errors": "2.0.0", 1023 | "iconv-lite": "0.4.24", 1024 | "on-finished": "2.4.1", 1025 | "qs": "6.11.0", 1026 | "raw-body": "2.5.1", 1027 | "type-is": "~1.6.18", 1028 | "unpipe": "1.0.0" 1029 | } 1030 | }, 1031 | "bowser": { 1032 | "version": "2.11.0", 1033 | "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", 1034 | "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", 1035 | "optional": true 1036 | }, 1037 | "bson": { 1038 | "version": "4.7.0", 1039 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.0.tgz", 1040 | "integrity": "sha512-VrlEE4vuiO1WTpfof4VmaVolCVYkYTgB9iWgYNOrVlnifpME/06fhFRmONgBhClD5pFC1t9ZWqFUQEQAzY43bA==", 1041 | "requires": { 1042 | "buffer": "^5.6.0" 1043 | } 1044 | }, 1045 | "buffer": { 1046 | "version": "5.7.1", 1047 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 1048 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 1049 | "requires": { 1050 | "base64-js": "^1.3.1", 1051 | "ieee754": "^1.1.13" 1052 | } 1053 | }, 1054 | "buffer-equal-constant-time": { 1055 | "version": "1.0.1", 1056 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 1057 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 1058 | }, 1059 | "bytes": { 1060 | "version": "3.1.2", 1061 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 1062 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 1063 | }, 1064 | "call-bind": { 1065 | "version": "1.0.2", 1066 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 1067 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 1068 | "requires": { 1069 | "function-bind": "^1.1.1", 1070 | "get-intrinsic": "^1.0.2" 1071 | } 1072 | }, 1073 | "content-disposition": { 1074 | "version": "0.5.4", 1075 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 1076 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 1077 | "requires": { 1078 | "safe-buffer": "5.2.1" 1079 | } 1080 | }, 1081 | "content-type": { 1082 | "version": "1.0.4", 1083 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 1084 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 1085 | }, 1086 | "cookie": { 1087 | "version": "0.4.1", 1088 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", 1089 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" 1090 | }, 1091 | "cookie-parser": { 1092 | "version": "1.4.6", 1093 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", 1094 | "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", 1095 | "requires": { 1096 | "cookie": "0.4.1", 1097 | "cookie-signature": "1.0.6" 1098 | } 1099 | }, 1100 | "cookie-signature": { 1101 | "version": "1.0.6", 1102 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 1103 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 1104 | }, 1105 | "cors": { 1106 | "version": "2.8.5", 1107 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 1108 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 1109 | "requires": { 1110 | "object-assign": "^4", 1111 | "vary": "^1" 1112 | } 1113 | }, 1114 | "debug": { 1115 | "version": "2.6.9", 1116 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1117 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1118 | "requires": { 1119 | "ms": "2.0.0" 1120 | } 1121 | }, 1122 | "depd": { 1123 | "version": "2.0.0", 1124 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1125 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 1126 | }, 1127 | "destroy": { 1128 | "version": "1.2.0", 1129 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 1130 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 1131 | }, 1132 | "dotenv": { 1133 | "version": "16.0.3", 1134 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 1135 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 1136 | }, 1137 | "ecdsa-sig-formatter": { 1138 | "version": "1.0.11", 1139 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 1140 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 1141 | "requires": { 1142 | "safe-buffer": "^5.0.1" 1143 | } 1144 | }, 1145 | "ee-first": { 1146 | "version": "1.1.1", 1147 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 1148 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 1149 | }, 1150 | "encodeurl": { 1151 | "version": "1.0.2", 1152 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1153 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 1154 | }, 1155 | "engine.io": { 1156 | "version": "6.4.1", 1157 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", 1158 | "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", 1159 | "requires": { 1160 | "@types/cookie": "^0.4.1", 1161 | "@types/cors": "^2.8.12", 1162 | "@types/node": ">=10.0.0", 1163 | "accepts": "~1.3.4", 1164 | "base64id": "2.0.0", 1165 | "cookie": "~0.4.1", 1166 | "cors": "~2.8.5", 1167 | "debug": "~4.3.1", 1168 | "engine.io-parser": "~5.0.3", 1169 | "ws": "~8.11.0" 1170 | }, 1171 | "dependencies": { 1172 | "debug": { 1173 | "version": "4.3.4", 1174 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1175 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1176 | "requires": { 1177 | "ms": "2.1.2" 1178 | } 1179 | }, 1180 | "ms": { 1181 | "version": "2.1.2", 1182 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1183 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1184 | } 1185 | } 1186 | }, 1187 | "engine.io-parser": { 1188 | "version": "5.0.6", 1189 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", 1190 | "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==" 1191 | }, 1192 | "escape-html": { 1193 | "version": "1.0.3", 1194 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 1195 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 1196 | }, 1197 | "etag": { 1198 | "version": "1.8.1", 1199 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 1200 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 1201 | }, 1202 | "express": { 1203 | "version": "4.18.2", 1204 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 1205 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 1206 | "requires": { 1207 | "accepts": "~1.3.8", 1208 | "array-flatten": "1.1.1", 1209 | "body-parser": "1.20.1", 1210 | "content-disposition": "0.5.4", 1211 | "content-type": "~1.0.4", 1212 | "cookie": "0.5.0", 1213 | "cookie-signature": "1.0.6", 1214 | "debug": "2.6.9", 1215 | "depd": "2.0.0", 1216 | "encodeurl": "~1.0.2", 1217 | "escape-html": "~1.0.3", 1218 | "etag": "~1.8.1", 1219 | "finalhandler": "1.2.0", 1220 | "fresh": "0.5.2", 1221 | "http-errors": "2.0.0", 1222 | "merge-descriptors": "1.0.1", 1223 | "methods": "~1.1.2", 1224 | "on-finished": "2.4.1", 1225 | "parseurl": "~1.3.3", 1226 | "path-to-regexp": "0.1.7", 1227 | "proxy-addr": "~2.0.7", 1228 | "qs": "6.11.0", 1229 | "range-parser": "~1.2.1", 1230 | "safe-buffer": "5.2.1", 1231 | "send": "0.18.0", 1232 | "serve-static": "1.15.0", 1233 | "setprototypeof": "1.2.0", 1234 | "statuses": "2.0.1", 1235 | "type-is": "~1.6.18", 1236 | "utils-merge": "1.0.1", 1237 | "vary": "~1.1.2" 1238 | }, 1239 | "dependencies": { 1240 | "cookie": { 1241 | "version": "0.5.0", 1242 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 1243 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" 1244 | } 1245 | } 1246 | }, 1247 | "extend": { 1248 | "version": "3.0.2", 1249 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 1250 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 1251 | }, 1252 | "fast-text-encoding": { 1253 | "version": "1.0.6", 1254 | "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", 1255 | "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==" 1256 | }, 1257 | "fast-xml-parser": { 1258 | "version": "4.0.11", 1259 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", 1260 | "integrity": "sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA==", 1261 | "optional": true, 1262 | "requires": { 1263 | "strnum": "^1.0.5" 1264 | } 1265 | }, 1266 | "finalhandler": { 1267 | "version": "1.2.0", 1268 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 1269 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 1270 | "requires": { 1271 | "debug": "2.6.9", 1272 | "encodeurl": "~1.0.2", 1273 | "escape-html": "~1.0.3", 1274 | "on-finished": "2.4.1", 1275 | "parseurl": "~1.3.3", 1276 | "statuses": "2.0.1", 1277 | "unpipe": "~1.0.0" 1278 | } 1279 | }, 1280 | "forwarded": { 1281 | "version": "0.2.0", 1282 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 1283 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 1284 | }, 1285 | "fresh": { 1286 | "version": "0.5.2", 1287 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1288 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 1289 | }, 1290 | "function-bind": { 1291 | "version": "1.1.1", 1292 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1293 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 1294 | }, 1295 | "gaxios": { 1296 | "version": "5.0.2", 1297 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.2.tgz", 1298 | "integrity": "sha512-TjtV2AJOZoMQqRYoy5eM8cCQogYwazWNYLQ72QB0kwa6vHHruYkGmhhyrlzbmgNHK1dNnuP2WSH81urfzyN2Og==", 1299 | "requires": { 1300 | "extend": "^3.0.2", 1301 | "https-proxy-agent": "^5.0.0", 1302 | "is-stream": "^2.0.0", 1303 | "node-fetch": "^2.6.7" 1304 | } 1305 | }, 1306 | "gcp-metadata": { 1307 | "version": "5.2.0", 1308 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz", 1309 | "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==", 1310 | "requires": { 1311 | "gaxios": "^5.0.0", 1312 | "json-bigint": "^1.0.0" 1313 | } 1314 | }, 1315 | "get-intrinsic": { 1316 | "version": "1.1.3", 1317 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 1318 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 1319 | "requires": { 1320 | "function-bind": "^1.1.1", 1321 | "has": "^1.0.3", 1322 | "has-symbols": "^1.0.3" 1323 | } 1324 | }, 1325 | "google-auth-library": { 1326 | "version": "8.7.0", 1327 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.7.0.tgz", 1328 | "integrity": "sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==", 1329 | "requires": { 1330 | "arrify": "^2.0.0", 1331 | "base64-js": "^1.3.0", 1332 | "ecdsa-sig-formatter": "^1.0.11", 1333 | "fast-text-encoding": "^1.0.0", 1334 | "gaxios": "^5.0.0", 1335 | "gcp-metadata": "^5.0.0", 1336 | "gtoken": "^6.1.0", 1337 | "jws": "^4.0.0", 1338 | "lru-cache": "^6.0.0" 1339 | }, 1340 | "dependencies": { 1341 | "jwa": { 1342 | "version": "2.0.0", 1343 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 1344 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 1345 | "requires": { 1346 | "buffer-equal-constant-time": "1.0.1", 1347 | "ecdsa-sig-formatter": "1.0.11", 1348 | "safe-buffer": "^5.0.1" 1349 | } 1350 | }, 1351 | "jws": { 1352 | "version": "4.0.0", 1353 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 1354 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 1355 | "requires": { 1356 | "jwa": "^2.0.0", 1357 | "safe-buffer": "^5.0.1" 1358 | } 1359 | } 1360 | } 1361 | }, 1362 | "google-p12-pem": { 1363 | "version": "4.0.1", 1364 | "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", 1365 | "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", 1366 | "requires": { 1367 | "node-forge": "^1.3.1" 1368 | } 1369 | }, 1370 | "gtoken": { 1371 | "version": "6.1.2", 1372 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", 1373 | "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", 1374 | "requires": { 1375 | "gaxios": "^5.0.1", 1376 | "google-p12-pem": "^4.0.0", 1377 | "jws": "^4.0.0" 1378 | }, 1379 | "dependencies": { 1380 | "jwa": { 1381 | "version": "2.0.0", 1382 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 1383 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 1384 | "requires": { 1385 | "buffer-equal-constant-time": "1.0.1", 1386 | "ecdsa-sig-formatter": "1.0.11", 1387 | "safe-buffer": "^5.0.1" 1388 | } 1389 | }, 1390 | "jws": { 1391 | "version": "4.0.0", 1392 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 1393 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 1394 | "requires": { 1395 | "jwa": "^2.0.0", 1396 | "safe-buffer": "^5.0.1" 1397 | } 1398 | } 1399 | } 1400 | }, 1401 | "has": { 1402 | "version": "1.0.3", 1403 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1404 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1405 | "requires": { 1406 | "function-bind": "^1.1.1" 1407 | } 1408 | }, 1409 | "has-symbols": { 1410 | "version": "1.0.3", 1411 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 1412 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 1413 | }, 1414 | "http-errors": { 1415 | "version": "2.0.0", 1416 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 1417 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 1418 | "requires": { 1419 | "depd": "2.0.0", 1420 | "inherits": "2.0.4", 1421 | "setprototypeof": "1.2.0", 1422 | "statuses": "2.0.1", 1423 | "toidentifier": "1.0.1" 1424 | } 1425 | }, 1426 | "https-proxy-agent": { 1427 | "version": "5.0.1", 1428 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 1429 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 1430 | "requires": { 1431 | "agent-base": "6", 1432 | "debug": "4" 1433 | }, 1434 | "dependencies": { 1435 | "debug": { 1436 | "version": "4.3.4", 1437 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1438 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1439 | "requires": { 1440 | "ms": "2.1.2" 1441 | } 1442 | }, 1443 | "ms": { 1444 | "version": "2.1.2", 1445 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1446 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1447 | } 1448 | } 1449 | }, 1450 | "iconv-lite": { 1451 | "version": "0.4.24", 1452 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1453 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1454 | "requires": { 1455 | "safer-buffer": ">= 2.1.2 < 3" 1456 | } 1457 | }, 1458 | "ieee754": { 1459 | "version": "1.2.1", 1460 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1461 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1462 | }, 1463 | "inherits": { 1464 | "version": "2.0.4", 1465 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1466 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1467 | }, 1468 | "ip": { 1469 | "version": "2.0.0", 1470 | "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", 1471 | "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" 1472 | }, 1473 | "ipaddr.js": { 1474 | "version": "1.9.1", 1475 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1476 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 1477 | }, 1478 | "is-stream": { 1479 | "version": "2.0.1", 1480 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", 1481 | "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" 1482 | }, 1483 | "json-bigint": { 1484 | "version": "1.0.0", 1485 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", 1486 | "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", 1487 | "requires": { 1488 | "bignumber.js": "^9.0.0" 1489 | } 1490 | }, 1491 | "jsonwebtoken": { 1492 | "version": "9.0.0", 1493 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", 1494 | "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", 1495 | "requires": { 1496 | "jws": "^3.2.2", 1497 | "lodash": "^4.17.21", 1498 | "ms": "^2.1.1", 1499 | "semver": "^7.3.8" 1500 | }, 1501 | "dependencies": { 1502 | "ms": { 1503 | "version": "2.1.3", 1504 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1505 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1506 | } 1507 | } 1508 | }, 1509 | "jwa": { 1510 | "version": "1.4.1", 1511 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 1512 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 1513 | "requires": { 1514 | "buffer-equal-constant-time": "1.0.1", 1515 | "ecdsa-sig-formatter": "1.0.11", 1516 | "safe-buffer": "^5.0.1" 1517 | } 1518 | }, 1519 | "jws": { 1520 | "version": "3.2.2", 1521 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 1522 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 1523 | "requires": { 1524 | "jwa": "^1.4.1", 1525 | "safe-buffer": "^5.0.1" 1526 | } 1527 | }, 1528 | "jwt-check-expiration": { 1529 | "version": "1.0.5", 1530 | "resolved": "https://registry.npmjs.org/jwt-check-expiration/-/jwt-check-expiration-1.0.5.tgz", 1531 | "integrity": "sha512-Ov2A7f/zwiZ8wvi6KG+Jeb6am1pvwgcms+HytB4GMPzrCf5UfytnlBVRh9a39Hi2v91H3aETtXHZreB8BTdbUQ==", 1532 | "requires": { 1533 | "jwt-decode": "^2.2.0" 1534 | }, 1535 | "dependencies": { 1536 | "jwt-decode": { 1537 | "version": "2.2.0", 1538 | "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", 1539 | "integrity": "sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ==" 1540 | } 1541 | } 1542 | }, 1543 | "jwt-decode": { 1544 | "version": "3.1.2", 1545 | "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", 1546 | "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" 1547 | }, 1548 | "kareem": { 1549 | "version": "2.5.0", 1550 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.0.tgz", 1551 | "integrity": "sha512-rVBUGGwvqg130iwYu8k7lutHuDBFj1yGRdnlE44wEhxAmFBad1zcL66PdWC1raw3tIObY6XWhtv3VL04xQb/cg==" 1552 | }, 1553 | "lodash": { 1554 | "version": "4.17.21", 1555 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1556 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 1557 | }, 1558 | "lru-cache": { 1559 | "version": "6.0.0", 1560 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1561 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1562 | "requires": { 1563 | "yallist": "^4.0.0" 1564 | } 1565 | }, 1566 | "media-typer": { 1567 | "version": "0.3.0", 1568 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1569 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 1570 | }, 1571 | "memory-pager": { 1572 | "version": "1.5.0", 1573 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1574 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 1575 | "optional": true 1576 | }, 1577 | "merge-descriptors": { 1578 | "version": "1.0.1", 1579 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1580 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 1581 | }, 1582 | "methods": { 1583 | "version": "1.1.2", 1584 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1585 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 1586 | }, 1587 | "mime": { 1588 | "version": "1.6.0", 1589 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1590 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1591 | }, 1592 | "mime-db": { 1593 | "version": "1.52.0", 1594 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1595 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 1596 | }, 1597 | "mime-types": { 1598 | "version": "2.1.35", 1599 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1600 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1601 | "requires": { 1602 | "mime-db": "1.52.0" 1603 | } 1604 | }, 1605 | "mongodb": { 1606 | "version": "4.12.1", 1607 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.12.1.tgz", 1608 | "integrity": "sha512-koT87tecZmxPKtxRQD8hCKfn+ockEL2xBiUvx3isQGI6mFmagWt4f4AyCE9J4sKepnLhMacoCTQQA6SLAI2L6w==", 1609 | "requires": { 1610 | "@aws-sdk/credential-providers": "^3.186.0", 1611 | "bson": "^4.7.0", 1612 | "mongodb-connection-string-url": "^2.5.4", 1613 | "saslprep": "^1.0.3", 1614 | "socks": "^2.7.1" 1615 | } 1616 | }, 1617 | "mongodb-connection-string-url": { 1618 | "version": "2.6.0", 1619 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", 1620 | "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", 1621 | "requires": { 1622 | "@types/whatwg-url": "^8.2.1", 1623 | "whatwg-url": "^11.0.0" 1624 | } 1625 | }, 1626 | "mongoose": { 1627 | "version": "6.8.2", 1628 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.8.2.tgz", 1629 | "integrity": "sha512-cIato5N2w/QuJkkh0w4nyf7ty7DqmmP/W8/6PFSM0DrzbxIMlr6VN15LBIceTSJIxbznNl2Mlbh9Rm4sokMw+A==", 1630 | "requires": { 1631 | "bson": "^4.7.0", 1632 | "kareem": "2.5.0", 1633 | "mongodb": "4.12.1", 1634 | "mpath": "0.9.0", 1635 | "mquery": "4.0.3", 1636 | "ms": "2.1.3", 1637 | "sift": "16.0.1" 1638 | }, 1639 | "dependencies": { 1640 | "ms": { 1641 | "version": "2.1.3", 1642 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1643 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1644 | } 1645 | } 1646 | }, 1647 | "mpath": { 1648 | "version": "0.9.0", 1649 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", 1650 | "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" 1651 | }, 1652 | "mquery": { 1653 | "version": "4.0.3", 1654 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", 1655 | "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", 1656 | "requires": { 1657 | "debug": "4.x" 1658 | }, 1659 | "dependencies": { 1660 | "debug": { 1661 | "version": "4.3.4", 1662 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1663 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1664 | "requires": { 1665 | "ms": "2.1.2" 1666 | } 1667 | }, 1668 | "ms": { 1669 | "version": "2.1.2", 1670 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1671 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1672 | } 1673 | } 1674 | }, 1675 | "ms": { 1676 | "version": "2.0.0", 1677 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1678 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1679 | }, 1680 | "negotiator": { 1681 | "version": "0.6.3", 1682 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1683 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 1684 | }, 1685 | "node-fetch": { 1686 | "version": "2.6.8", 1687 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", 1688 | "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", 1689 | "requires": { 1690 | "whatwg-url": "^5.0.0" 1691 | }, 1692 | "dependencies": { 1693 | "tr46": { 1694 | "version": "0.0.3", 1695 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1696 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1697 | }, 1698 | "webidl-conversions": { 1699 | "version": "3.0.1", 1700 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1701 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1702 | }, 1703 | "whatwg-url": { 1704 | "version": "5.0.0", 1705 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1706 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1707 | "requires": { 1708 | "tr46": "~0.0.3", 1709 | "webidl-conversions": "^3.0.0" 1710 | } 1711 | } 1712 | } 1713 | }, 1714 | "node-forge": { 1715 | "version": "1.3.1", 1716 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 1717 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" 1718 | }, 1719 | "object-assign": { 1720 | "version": "4.1.1", 1721 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1722 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1723 | }, 1724 | "object-inspect": { 1725 | "version": "1.12.2", 1726 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 1727 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" 1728 | }, 1729 | "on-finished": { 1730 | "version": "2.4.1", 1731 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1732 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1733 | "requires": { 1734 | "ee-first": "1.1.1" 1735 | } 1736 | }, 1737 | "parseurl": { 1738 | "version": "1.3.3", 1739 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1740 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 1741 | }, 1742 | "path-to-regexp": { 1743 | "version": "0.1.7", 1744 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1745 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 1746 | }, 1747 | "proxy-addr": { 1748 | "version": "2.0.7", 1749 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1750 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1751 | "requires": { 1752 | "forwarded": "0.2.0", 1753 | "ipaddr.js": "1.9.1" 1754 | } 1755 | }, 1756 | "punycode": { 1757 | "version": "2.1.1", 1758 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1759 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1760 | }, 1761 | "qs": { 1762 | "version": "6.11.0", 1763 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1764 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1765 | "requires": { 1766 | "side-channel": "^1.0.4" 1767 | } 1768 | }, 1769 | "range-parser": { 1770 | "version": "1.2.1", 1771 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1772 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1773 | }, 1774 | "raw-body": { 1775 | "version": "2.5.1", 1776 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 1777 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 1778 | "requires": { 1779 | "bytes": "3.1.2", 1780 | "http-errors": "2.0.0", 1781 | "iconv-lite": "0.4.24", 1782 | "unpipe": "1.0.0" 1783 | } 1784 | }, 1785 | "safe-buffer": { 1786 | "version": "5.2.1", 1787 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1788 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1789 | }, 1790 | "safer-buffer": { 1791 | "version": "2.1.2", 1792 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1793 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1794 | }, 1795 | "saslprep": { 1796 | "version": "1.0.3", 1797 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 1798 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 1799 | "optional": true, 1800 | "requires": { 1801 | "sparse-bitfield": "^3.0.3" 1802 | } 1803 | }, 1804 | "semver": { 1805 | "version": "7.3.8", 1806 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", 1807 | "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", 1808 | "requires": { 1809 | "lru-cache": "^6.0.0" 1810 | } 1811 | }, 1812 | "send": { 1813 | "version": "0.18.0", 1814 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1815 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1816 | "requires": { 1817 | "debug": "2.6.9", 1818 | "depd": "2.0.0", 1819 | "destroy": "1.2.0", 1820 | "encodeurl": "~1.0.2", 1821 | "escape-html": "~1.0.3", 1822 | "etag": "~1.8.1", 1823 | "fresh": "0.5.2", 1824 | "http-errors": "2.0.0", 1825 | "mime": "1.6.0", 1826 | "ms": "2.1.3", 1827 | "on-finished": "2.4.1", 1828 | "range-parser": "~1.2.1", 1829 | "statuses": "2.0.1" 1830 | }, 1831 | "dependencies": { 1832 | "ms": { 1833 | "version": "2.1.3", 1834 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1835 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1836 | } 1837 | } 1838 | }, 1839 | "serve-static": { 1840 | "version": "1.15.0", 1841 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1842 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1843 | "requires": { 1844 | "encodeurl": "~1.0.2", 1845 | "escape-html": "~1.0.3", 1846 | "parseurl": "~1.3.3", 1847 | "send": "0.18.0" 1848 | } 1849 | }, 1850 | "setprototypeof": { 1851 | "version": "1.2.0", 1852 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1853 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1854 | }, 1855 | "side-channel": { 1856 | "version": "1.0.4", 1857 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1858 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1859 | "requires": { 1860 | "call-bind": "^1.0.0", 1861 | "get-intrinsic": "^1.0.2", 1862 | "object-inspect": "^1.9.0" 1863 | } 1864 | }, 1865 | "sift": { 1866 | "version": "16.0.1", 1867 | "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", 1868 | "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" 1869 | }, 1870 | "smart-buffer": { 1871 | "version": "4.2.0", 1872 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 1873 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" 1874 | }, 1875 | "socket.io": { 1876 | "version": "4.6.1", 1877 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", 1878 | "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", 1879 | "requires": { 1880 | "accepts": "~1.3.4", 1881 | "base64id": "~2.0.0", 1882 | "debug": "~4.3.2", 1883 | "engine.io": "~6.4.1", 1884 | "socket.io-adapter": "~2.5.2", 1885 | "socket.io-parser": "~4.2.1" 1886 | }, 1887 | "dependencies": { 1888 | "debug": { 1889 | "version": "4.3.4", 1890 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1891 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1892 | "requires": { 1893 | "ms": "2.1.2" 1894 | } 1895 | }, 1896 | "ms": { 1897 | "version": "2.1.2", 1898 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1899 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1900 | } 1901 | } 1902 | }, 1903 | "socket.io-adapter": { 1904 | "version": "2.5.2", 1905 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", 1906 | "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", 1907 | "requires": { 1908 | "ws": "~8.11.0" 1909 | } 1910 | }, 1911 | "socket.io-parser": { 1912 | "version": "4.2.2", 1913 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", 1914 | "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", 1915 | "requires": { 1916 | "@socket.io/component-emitter": "~3.1.0", 1917 | "debug": "~4.3.1" 1918 | }, 1919 | "dependencies": { 1920 | "debug": { 1921 | "version": "4.3.4", 1922 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1923 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1924 | "requires": { 1925 | "ms": "2.1.2" 1926 | } 1927 | }, 1928 | "ms": { 1929 | "version": "2.1.2", 1930 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1931 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1932 | } 1933 | } 1934 | }, 1935 | "socks": { 1936 | "version": "2.7.1", 1937 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", 1938 | "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", 1939 | "requires": { 1940 | "ip": "^2.0.0", 1941 | "smart-buffer": "^4.2.0" 1942 | } 1943 | }, 1944 | "sparse-bitfield": { 1945 | "version": "3.0.3", 1946 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1947 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 1948 | "optional": true, 1949 | "requires": { 1950 | "memory-pager": "^1.0.2" 1951 | } 1952 | }, 1953 | "statuses": { 1954 | "version": "2.0.1", 1955 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1956 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 1957 | }, 1958 | "strnum": { 1959 | "version": "1.0.5", 1960 | "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", 1961 | "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", 1962 | "optional": true 1963 | }, 1964 | "toidentifier": { 1965 | "version": "1.0.1", 1966 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1967 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 1968 | }, 1969 | "tr46": { 1970 | "version": "3.0.0", 1971 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", 1972 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", 1973 | "requires": { 1974 | "punycode": "^2.1.1" 1975 | } 1976 | }, 1977 | "tslib": { 1978 | "version": "2.4.1", 1979 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", 1980 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", 1981 | "optional": true 1982 | }, 1983 | "type-is": { 1984 | "version": "1.6.18", 1985 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1986 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1987 | "requires": { 1988 | "media-typer": "0.3.0", 1989 | "mime-types": "~2.1.24" 1990 | } 1991 | }, 1992 | "unpipe": { 1993 | "version": "1.0.0", 1994 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1995 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 1996 | }, 1997 | "utils-merge": { 1998 | "version": "1.0.1", 1999 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2000 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 2001 | }, 2002 | "uuid": { 2003 | "version": "8.3.2", 2004 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 2005 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 2006 | "optional": true 2007 | }, 2008 | "vary": { 2009 | "version": "1.1.2", 2010 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2011 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 2012 | }, 2013 | "webidl-conversions": { 2014 | "version": "7.0.0", 2015 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 2016 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" 2017 | }, 2018 | "whatwg-url": { 2019 | "version": "11.0.0", 2020 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", 2021 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", 2022 | "requires": { 2023 | "tr46": "^3.0.0", 2024 | "webidl-conversions": "^7.0.0" 2025 | } 2026 | }, 2027 | "ws": { 2028 | "version": "8.11.0", 2029 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 2030 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==" 2031 | }, 2032 | "yallist": { 2033 | "version": "4.0.0", 2034 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 2035 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 2036 | } 2037 | } 2038 | } 2039 | --------------------------------------------------------------------------------