├── Client ├── src │ ├── App.css │ ├── vite-env.d.ts │ ├── index.css │ ├── store.ts │ ├── App.tsx │ ├── main.tsx │ ├── pages │ │ ├── home.tsx │ │ ├── auth.tsx │ │ ├── joinroom.tsx │ │ ├── createroom.tsx │ │ └── room.tsx │ ├── components │ │ ├── members.tsx │ │ ├── result.tsx │ │ └── chats.tsx │ └── assets │ │ └── react.svg ├── .dockerignore ├── postcss.config.js ├── vercel.json ├── .env.example ├── vite.config.ts ├── tailwind.config.js ├── entrypoint.sh ├── tsconfig.node.json ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── tsconfig.json ├── Dockerfile ├── README.md ├── public │ └── vite.svg └── package.json ├── Worker ├── .gitignore ├── .dockerignore ├── .env.example ├── entrypoint.sh ├── package.json ├── Dockerfile ├── src │ └── index.ts ├── tsconfig.json └── package-lock.json ├── Server ├── .dockerignore ├── .gitignore ├── .env.example ├── entrypoint.sh ├── Dockerfile ├── package.json ├── src │ ├── index.ts │ └── utils │ │ └── roomManager.ts ├── tsconfig.json └── package-lock.json ├── SystemDesign.png ├── docker-compose.yml └── readme.md /Client/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Worker/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .env -------------------------------------------------------------------------------- /Client/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env.example 3 | dist -------------------------------------------------------------------------------- /Server/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env.example 3 | dist -------------------------------------------------------------------------------- /Worker/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env.example 3 | dist -------------------------------------------------------------------------------- /Server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .env 4 | rest.http -------------------------------------------------------------------------------- /Client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /SystemDesign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayush-oswal/Collaborative-code-editor/HEAD/SystemDesign.png -------------------------------------------------------------------------------- /Client/src/index.css: -------------------------------------------------------------------------------- 1 | 2 | /* index.css */ 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | -------------------------------------------------------------------------------- /Client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /Client/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /Client/.env.example: -------------------------------------------------------------------------------- 1 | VITE_REACT_APP_SERVER_URL = "" 2 | # NOTE: only provide the url after http or https eg ://localhost:8080 for local/docker server 3 | VITE_CLERK_PUBLISHABLE_KEY= "" -------------------------------------------------------------------------------- /Client/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /Worker/.env.example: -------------------------------------------------------------------------------- 1 | X_RAPID_API_KEY = "" 2 | # for local if you run a redis conatiner seperately 3 | # REDIS_URL = "redis://localhost:6379" 4 | # for docker if you use docker-compose up 5 | # REDIS_URL = "redis://redis:6379" -------------------------------------------------------------------------------- /Client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | 2 | /** @type {import('tailwindcss').Config} */ 3 | module.exports = { 4 | content: ["./src/**/*.{html,jsx,tsx}"], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | } 10 | -------------------------------------------------------------------------------- /Server/.env.example: -------------------------------------------------------------------------------- 1 | 2 | MONGO_URL = "" 3 | # for local if you run a redis conatiner seperately 4 | # REDIS_URL = "redis://localhost:6379" 5 | # for docker if you use docker-compose up 6 | # REDIS_URL = "redis://redis:6379" 7 | # for prod 8 | # REDIS_URL = "No-Url-provided" -------------------------------------------------------------------------------- /Client/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check if node_modules directory does not exist 4 | if [ ! -d "node_modules" ]; then 5 | # Install dependencies if node_modules is missing 6 | npm install 7 | fi 8 | 9 | # Execute the CMD passed as arguments to the script 10 | exec "$@" 11 | -------------------------------------------------------------------------------- /Server/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check if node_modules directory does not exist 4 | if [ ! -d "node_modules" ]; then 5 | # Install dependencies if node_modules is missing 6 | npm install 7 | fi 8 | 9 | # Execute the CMD passed as arguments to the script 10 | exec "$@" 11 | -------------------------------------------------------------------------------- /Worker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check if node_modules directory does not exist 4 | if [ ! -d "node_modules" ]; then 5 | # Install dependencies if node_modules is missing 6 | npm install 7 | fi 8 | 9 | # Execute the CMD passed as arguments to the script 10 | exec "$@" 11 | -------------------------------------------------------------------------------- /Client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /Client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | .env -------------------------------------------------------------------------------- /Client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Collaborative Code Editor - Ayush 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Client/src/store.ts: -------------------------------------------------------------------------------- 1 | // store.ts 2 | import {create} from 'zustand'; 3 | 4 | interface UserStore { 5 | username: string; 6 | roomID: string; 7 | setUsername: (username: string) => void; 8 | setRoomID: (roomID:string)=> void; 9 | } 10 | 11 | export const useUserStore = create((set) => ({ 12 | username: '', 13 | roomID: '', 14 | setUsername: (username: string) => set({ username }), 15 | setRoomID: (roomID : string) => set({roomID}) 16 | })); 17 | -------------------------------------------------------------------------------- /Client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /Worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker", 3 | "version": "1.0.0", 4 | "description": "worker node for code execution result", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": " tsc -b && node dist/index.js" 9 | }, 10 | "author": "ayush", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/node": "^20.12.12", 14 | "axios": "^1.7.2", 15 | "dotenv": "^16.4.5", 16 | "node-fetch": "^3.3.2", 17 | "redis": "^4.6.14" 18 | }, 19 | "devDependencies": { 20 | "typescript": "^5.4.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Worker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official node image as the base image 2 | FROM node:18 3 | 4 | # Set the working directory 5 | WORKDIR /src 6 | 7 | # Copy package.json and package-lock.json 8 | COPY package*.json ./ 9 | 10 | # Install dependencies 11 | RUN npm install 12 | 13 | # Copy the rest of the application code 14 | COPY . . 15 | 16 | # Copy the entrypoint script 17 | COPY entrypoint.sh . 18 | 19 | # Make the entrypoint script executable 20 | RUN chmod +x entrypoint.sh 21 | 22 | # Use the shell to run the entrypoint script 23 | ENTRYPOINT ["sh", "/src/entrypoint.sh"] 24 | 25 | # Command to run the worker 26 | CMD ["npm", "start"] 27 | -------------------------------------------------------------------------------- /Server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official node image as the base image 2 | FROM node:18 3 | 4 | # Set the working directory 5 | WORKDIR /src 6 | 7 | # Copy package.json and package-lock.json 8 | COPY package*.json ./ 9 | 10 | # Install dependencies 11 | RUN npm install 12 | 13 | # Copy the rest of the application code 14 | COPY . . 15 | 16 | # Expose the port the server runs on 17 | EXPOSE 8080 18 | 19 | # Copy the entrypoint script 20 | COPY entrypoint.sh . 21 | 22 | # Make the entrypoint script executable 23 | RUN chmod +x entrypoint.sh 24 | 25 | # Use the shell to run the entrypoint script 26 | ENTRYPOINT ["sh", "/src/entrypoint.sh"] 27 | 28 | # Command to run the server 29 | CMD ["npm", "start"] 30 | -------------------------------------------------------------------------------- /Client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /Client/src/App.tsx: -------------------------------------------------------------------------------- 1 | 2 | import './App.css' 3 | import { Route, Routes } from 'react-router-dom' 4 | import Room from './pages/room' 5 | import Auth from './pages/auth' 6 | import JoinRoom from './pages/joinroom' 7 | import CreateRoom from './pages/createroom' 8 | import Home from './pages/home' 9 | 10 | function App() { 11 | 12 | return ( 13 | <> 14 | 15 | }> 16 | }> 17 | }> 18 | }> 19 | }> 20 | 21 | 22 | ) 23 | } 24 | 25 | export default App 26 | -------------------------------------------------------------------------------- /Client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official node image as the base image 2 | FROM node:18 3 | 4 | # Set the working directory 5 | WORKDIR /src 6 | 7 | # Copy package.json and package-lock.json 8 | COPY package*.json ./ 9 | 10 | # Install dependencies 11 | RUN npm install 12 | 13 | RUN npm install @rollup/rollup-linux-x64-gnu 14 | 15 | # Copy the rest of the application code 16 | COPY . . 17 | 18 | # Expose the port the app runs on 19 | EXPOSE 5173 20 | 21 | # Copy the entrypoint script 22 | COPY entrypoint.sh . 23 | 24 | # Make the entrypoint script executable 25 | RUN chmod +x entrypoint.sh 26 | 27 | # Use the shell to run the entrypoint script 28 | ENTRYPOINT ["sh", "/src/entrypoint.sh"] 29 | 30 | # Command to run the app in development mode 31 | CMD ["npm", "run", "dev"] 32 | -------------------------------------------------------------------------------- /Client/src/main.tsx: -------------------------------------------------------------------------------- 1 | 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | import { BrowserRouter } from 'react-router-dom' 6 | import { ChakraProvider } from '@chakra-ui/react'; 7 | import { ClerkProvider } from '@clerk/clerk-react' 8 | 9 | // Import your publishable key 10 | const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY 11 | 12 | if (!PUBLISHABLE_KEY) { 13 | throw new Error("Missing Publishable Key") 14 | } 15 | 16 | ReactDOM.createRoot(document.getElementById('root')!).render( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | -------------------------------------------------------------------------------- /Server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "Web Socket Server", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": " tsc -b && node dist/index.js" 9 | }, 10 | "author": "Babu", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/bcrypt": "^5.0.2", 14 | "@types/express": "^4.17.21", 15 | "@types/ws": "^8.5.10", 16 | "bcrypt": "^5.1.1", 17 | "dotenv": "^16.4.5", 18 | "express": "^4.19.2", 19 | "mongoose": "^8.4.0", 20 | "redis": "^4.6.14", 21 | "ws": "^8.17.0" 22 | }, 23 | "devDependencies": { 24 | "@types/bcrypt": "^5.0.2", 25 | "@types/cors": "^2.8.17", 26 | "cors": "^2.8.5", 27 | "typescript": "^5.4.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | redis: 5 | image: "redis:latest" 6 | container_name: redis 7 | ports: 8 | - "6379:6379" 9 | volumes: 10 | - redis-data:/data 11 | 12 | server: 13 | build: 14 | context: ./Server 15 | dockerfile: Dockerfile 16 | container_name: server 17 | depends_on: 18 | - redis 19 | ports: 20 | - "8080:8080" 21 | 22 | worker: 23 | build: 24 | context: ./Worker 25 | dockerfile: Dockerfile 26 | container_name: worker 27 | depends_on: 28 | - redis 29 | 30 | client: 31 | build: 32 | context: ./Client 33 | dockerfile: Dockerfile 34 | container_name: client 35 | depends_on: 36 | - server 37 | ports: 38 | - "5173:5173" 39 | 40 | volumes: 41 | redis-data: 42 | -------------------------------------------------------------------------------- /Client/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | 4 | 5 | const Home: React.FC = () => { 6 | const Navigate = useNavigate() 7 | 8 | return ( 9 |
10 |

Welcome to CodeCollab

11 |

12 | CodeCollab is a collaborative code editor where you can create or join rooms, chat, write code together, and even run it in real-time. 13 |

14 | 20 |
21 | ); 22 | }; 23 | 24 | export default Home; 25 | -------------------------------------------------------------------------------- /Client/src/pages/auth.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { SignInButton, SignedOut, useUser } from '@clerk/clerk-react'; 3 | import { useUserStore } from '../store'; 4 | import { useNavigate } from 'react-router-dom'; 5 | 6 | const Auth: React.FC = () => { 7 | const { user } = useUser(); 8 | const { setUsername } = useUserStore() 9 | const Navigate = useNavigate(); 10 | 11 | useEffect(() => { 12 | if (user) { 13 | const clerkUsername = user.username || user.firstName || user.emailAddresses[0].emailAddress; 14 | setUsername(clerkUsername); 15 | Navigate("/join") 16 | } 17 | }, [user, setUsername]); 18 | 19 | return ( 20 |
21 | 22 | 23 |
24 | 25 |
26 |
27 | 28 |
29 | ); 30 | }; 31 | 32 | export default Auth; 33 | -------------------------------------------------------------------------------- /Client/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /Client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Client/src/components/members.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box, Button } from '@chakra-ui/react'; 3 | import toast from 'react-hot-toast'; 4 | 5 | interface MembersProps { 6 | users: string[]; 7 | roomId: string; 8 | onLeave: () => void; 9 | } 10 | 11 | const Members: React.FC = ({ users, roomId, onLeave }) => { 12 | const copyRoomIdToClipboard = () => { 13 | navigator.clipboard.writeText(roomId); 14 | toast.success('Room ID copied to clipboard!'); 15 | }; 16 | 17 | return ( 18 | 19 | 35 | {users.map((user, index) => ( 36 | {user} 37 | ))} 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export default Members; 48 | -------------------------------------------------------------------------------- /Client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --host", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/icons": "^2.1.1", 14 | "@chakra-ui/react": "^2.8.2", 15 | "@clerk/clerk-react": "^5.9.0", 16 | "@codemirror/lang-cpp": "^6.0.2", 17 | "@codemirror/lang-java": "^6.0.1", 18 | "@codemirror/lang-javascript": "^6.2.2", 19 | "@codemirror/lang-python": "^6.1.6", 20 | "@emotion/react": "^11.11.4", 21 | "@emotion/styled": "^11.11.5", 22 | "@types/node": "^20.12.11", 23 | "@uiw/react-codemirror": "^4.22.1", 24 | "framer-motion": "^11.2.0", 25 | "react": "^18.2.0", 26 | "react-dom": "^18.2.0", 27 | "react-hot-toast": "^2.4.1", 28 | "react-router-dom": "^6.23.1", 29 | "uuidv4": "^6.2.13", 30 | "zustand": "^4.5.2" 31 | }, 32 | "devDependencies": { 33 | "@types/react": "^18.2.66", 34 | "@types/react-dom": "^18.2.22", 35 | "@typescript-eslint/eslint-plugin": "^7.2.0", 36 | "@typescript-eslint/parser": "^7.2.0", 37 | "@vitejs/plugin-react": "^4.2.1", 38 | "autoprefixer": "^10.4.19", 39 | "dotenv": "^16.4.5", 40 | "eslint": "^8.57.0", 41 | "eslint-plugin-react-hooks": "^4.6.0", 42 | "eslint-plugin-react-refresh": "^0.4.6", 43 | "postcss": "^8.4.38", 44 | "tailwindcss": "^3.4.3", 45 | "typescript": "^5.2.2", 46 | "vite": "^5.2.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Client/src/components/result.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Modal, 4 | ModalOverlay, 5 | ModalContent, 6 | ModalHeader, 7 | ModalFooter, 8 | ModalBody, 9 | ModalCloseButton, 10 | Button, 11 | Text, 12 | useColorModeValue 13 | } from '@chakra-ui/react'; 14 | 15 | interface ResultMessage { 16 | Title: string; 17 | stdout: string; 18 | stderr: string | null; 19 | status: string; 20 | compile_output: string | null; 21 | } 22 | 23 | interface ResultModalProps { 24 | isOpen: boolean; 25 | onClose: () => void; 26 | resultMessage: ResultMessage; 27 | } 28 | 29 | const ResultModal: React.FC = ({ isOpen, onClose, resultMessage }) => { 30 | const bgColor = useColorModeValue('gray.800', 'gray.800'); 31 | const textColor = useColorModeValue('white', 'white'); 32 | 33 | return ( 34 | 35 | 36 | 37 | {resultMessage.Title} 38 | 39 | 40 | Status: {resultMessage.status} 41 | Output: {resultMessage.stdout} 42 | { resultMessage.compile_output && Compile Output: {resultMessage.compile_output}} 43 | {resultMessage.stderr && Error: {resultMessage.stderr}} 44 | 45 | 46 | 49 | 50 | 51 | 52 | ); 53 | }; 54 | 55 | export default ResultModal; 56 | -------------------------------------------------------------------------------- /Client/src/components/chats.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Box, Flex, Input, IconButton } from "@chakra-ui/react"; 3 | import { ArrowForwardIcon } from "@chakra-ui/icons"; 4 | 5 | type chat = { 6 | username: string; 7 | message: string; 8 | }; 9 | 10 | interface chatProps { 11 | chats: chat[]; 12 | addChat: (msg: string) => void; 13 | } 14 | 15 | const Chats: React.FC = ({ chats, addChat }) => { 16 | const [msg, setMsg] = useState(""); 17 | 18 | const handleSendMessage = () => { 19 | if (msg.trim()) { 20 | addChat(msg); 21 | setMsg(""); 22 | } 23 | }; 24 | 25 | return ( 26 | 27 | 34 | {chats.map((chat, index) => ( 35 | 36 | {chat.username} 37 | {chat.message} 38 | 39 | ))} 40 | 41 | 42 | setMsg(e.target.value)} 45 | placeholder="Type a message..." 46 | bg="gray.700" 47 | color="blue.200" 48 | border="none" 49 | _placeholder={{ color: "blue.400" }} 50 | flex="1" 51 | /> 52 | } 55 | onClick={handleSendMessage} 56 | ml="2" 57 | bg="blue.500" 58 | _hover={{ bg: "blue.600" }} 59 | /> 60 | 61 | 62 | ); 63 | }; 64 | 65 | export default Chats; 66 | -------------------------------------------------------------------------------- /Client/src/pages/joinroom.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | import {useUserStore} from '../store' 4 | import { UserButton, useUser } from '@clerk/clerk-react'; 5 | 6 | const JoinRoom: React.FC = () => { 7 | const Navigate = useNavigate() 8 | const { roomID } = useUserStore(); 9 | const [roomId,setRoomId] = useState("") 10 | const {user} = useUser() 11 | useEffect(()=>{ 12 | if(!user?.username){ 13 | Navigate("/auth") 14 | } 15 | if(roomID!="") setRoomId(roomID) 16 | }) 17 | const handleSubmit = (event: React.FormEvent) => { 18 | event.preventDefault(); 19 | Navigate(`/room/${roomId}`) 20 | }; 21 | 22 | return ( 23 |
24 |
25 |
26 |

Hello, {user?.username}

27 |
28 | 29 |
30 |

Join Room

31 |
32 | {setRoomId(e.target.value)}} 38 | /> 39 | 45 |
46 |

47 | Don't have a room?{' '} 48 | 51 |

52 |
53 | ); 54 | }; 55 | 56 | export default JoinRoom; 57 | -------------------------------------------------------------------------------- /Server/src/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { createServer } from 'http'; 3 | import { Request, Response } from 'express' 4 | import { WebSocketServer } from 'ws' 5 | import { createClient } from 'redis' 6 | import cors from 'cors' 7 | import { RoomManager } from './utils/roomManager' 8 | import dotenv from "dotenv"; 9 | 10 | dotenv.config(); 11 | 12 | const app = express() 13 | const httpServer = createServer(app); 14 | 15 | app.use(cors()) 16 | app.use(express.json()); 17 | 18 | httpServer.listen(8080, () => { 19 | console.log(`Server listening on port 8080`); 20 | }); 21 | 22 | app.get("/",(req:Request,res:Response)=>{ 23 | res.status(200).json({message:"Hello world"}) 24 | }) 25 | 26 | //connect to redis after launching it from docker 27 | 28 | const redis_url = process.env.REDIS_URL === "No-Url-provided" ? "" : process.env.REDIS_URL 29 | 30 | const redisClient = createClient({ 31 | url: redis_url 32 | }); 33 | 34 | const redisClientSubscribing = createClient({ 35 | url: redis_url 36 | }); 37 | 38 | 39 | redisClient.connect().catch(err=>{console.log(err)}) 40 | redisClientSubscribing.connect().catch(err=>{console.log(err)}) 41 | 42 | 43 | const wss = new WebSocketServer({ server: httpServer }); 44 | 45 | wss.on('connection', function connection(ws) { 46 | ws.on('error', console.error); 47 | 48 | ws.on('message', function message(data, isBinary) { 49 | const message = JSON.parse(data.toString()); 50 | if(message.Title==="User-joined"){ 51 | RoomManager.getInstance().handleUserJoined(message, ws); 52 | } 53 | else if(message.Title==="User-left"){ 54 | RoomManager.getInstance().handleUserLeft(message) 55 | } 56 | else if(message.Title==="New-chat"){ 57 | RoomManager.getInstance().handleNewChat(message) 58 | } 59 | else if(message.Title==="lang-change"){ 60 | RoomManager.getInstance().handleLangChange(message) 61 | } 62 | else if(message.Title==="Code-change"){ 63 | RoomManager.getInstance().handleCodeChange(message) 64 | } 65 | else if(message.Title==="Submitted"){ 66 | RoomManager.getInstance().handleSubmitted(message,ws) 67 | } 68 | }); 69 | 70 | ws.send(JSON.stringify({Title : "Greet" , msg:'Hello! Message From Server!!'})); 71 | }); 72 | 73 | app.post("/create",(req:Request,res:Response)=>{ 74 | 75 | const {username, roomName, roomId} = req.body; 76 | if(!username || !roomName || !roomId){ 77 | res.status(400).json({error : "Some error"}) 78 | return; 79 | } 80 | 81 | try{ 82 | RoomManager.getInstance().create(req.body) 83 | res.status(200).json({ message: 'Room created successfully' }); 84 | } 85 | catch(e){ 86 | res.status(500).json({ message: 'Server error' }); 87 | } 88 | 89 | }) -------------------------------------------------------------------------------- /Client/src/pages/createroom.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { v4 as uuidv4 } from 'uuid'; 3 | import { useNavigate } from 'react-router-dom'; 4 | import { useUserStore } from '../store'; 5 | import { useUser } from '@clerk/clerk-react'; 6 | import { Spinner } from '@chakra-ui/react'; 7 | 8 | const CreateRoom: React.FC = () => { 9 | const [roomName, setRoomName] = useState(''); 10 | const [roomId, setRoomId] = useState(''); 11 | const [loading, setLoading] = useState(false); // New state for spinner 12 | const Navigate = useNavigate(); 13 | const { setRoomID } = useUserStore(); 14 | const { user } = useUser(); 15 | const username = user?.username; 16 | 17 | useEffect(() => { 18 | if (username == '') { 19 | Navigate('/auth'); 20 | } 21 | }); 22 | 23 | const handleGenerateRoomId = () => { 24 | const newRoomId = uuidv4(); 25 | setRoomId(newRoomId); 26 | }; 27 | 28 | const handleSubmit = async (event: React.FormEvent) => { 29 | event.preventDefault(); 30 | if (!roomId || !roomName) { 31 | alert('Fill name and id'); 32 | return; 33 | } 34 | setLoading(true); 35 | const serverUrl = import.meta.env.VITE_REACT_APP_SERVER_URL; 36 | let url = ''; 37 | if (serverUrl === '://localhost:8080') { 38 | url = `http${serverUrl}`; 39 | } else { 40 | url = `https${serverUrl}`; 41 | } 42 | try { 43 | const response = await fetch(`${url}/create`, { 44 | method: 'POST', 45 | headers: { 46 | 'Content-Type': 'application/json', 47 | }, 48 | body: JSON.stringify({ 49 | username, 50 | roomName, 51 | roomId, 52 | }), 53 | }); 54 | setLoading(false); 55 | if (response.ok) { 56 | // Add a toast here 57 | setRoomID(roomId); 58 | Navigate('/join'); 59 | } else { 60 | alert('Error creating room'); 61 | } 62 | } catch (err) { 63 | console.log(err); 64 | } 65 | }; 66 | 67 | return ( 68 |
69 |

Create Room

70 |
71 | setRoomName(e.target.value)} 76 | className="bg-blue-900 text-blue-200 border-b border-blue-400 rounded mb-4 px-3 py-2" 77 | /> 78 |
79 | 85 | 92 |
93 | 94 | {roomId && !loading && ( 95 | 101 | )} 102 | 103 | {loading && ( 104 | 105 | )} 106 | 107 |
108 | ); 109 | }; 110 | 111 | export default CreateRoom; 112 | -------------------------------------------------------------------------------- /Client/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Worker/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "redis"; 2 | import axios from "axios"; 3 | import dotenv from "dotenv"; 4 | 5 | 6 | dotenv.config(); 7 | 8 | const redisClient = createClient({ 9 | url: process.env.REDIS_URL 10 | }); 11 | 12 | 13 | async function processSubmission(submission: string) { 14 | const { roomId, code, language } = JSON.parse(submission); 15 | console.log(roomId, code, language); 16 | 17 | const judge0Url = 'https://judge0.p.rapidapi.com/submissions?fields=*&base64_encoded=false&wait=true' 18 | const judge0Key = process.env.X_RAPID_API_KEY || "" 19 | 20 | const payload = { 21 | source_code: code, 22 | language_id: getLanguageId(language), 23 | }; 24 | 25 | try { 26 | const response = await axios.post(judge0Url, payload, { 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | 'x-rapidapi-host': 'judge0-ce.p.rapidapi.com', 30 | 'x-rapidapi-key': judge0Key, 31 | }, 32 | }); 33 | 34 | if (!response.data || !response.data.status || response.data.status.id <= 2) { 35 | throw new Error(`Judge0 API responded with status ${response.data.status}`); 36 | } 37 | 38 | let result = response.data; 39 | 40 | if (result.status.id <= 2) { 41 | //means still processing 42 | result = await pollJudge0Result(result.token, judge0Key); 43 | } 44 | 45 | await redisClient.publish(roomId, JSON.stringify(result)); 46 | console.log(`Published result to room: ${roomId}`); 47 | } catch (error) { 48 | console.error(`Error processing submission for room: ${roomId}`, error); 49 | await redisClient.publish(roomId,"error"); 50 | } 51 | } 52 | 53 | async function pollJudge0Result(token: string, apiKey: string) { 54 | const judge0ResultUrl = `https://judge0.p.rapidapi.com/submissions/${token}?base64_encoded=false`; 55 | 56 | while (true) { 57 | try { 58 | const response = await axios.get(judge0ResultUrl, { 59 | headers: { 60 | 'Content-Type': 'application/json', 61 | 'x-rapidapi-host': 'judge0-ce.p.rapidapi.com', 62 | 'x-rapidapi-key': apiKey, 63 | }, 64 | }); 65 | 66 | if (!response.data || !response.data.status) { 67 | throw new Error(`Invalid response from Judge0 API`); 68 | } 69 | 70 | const resultData = response.data; 71 | 72 | if (resultData.status.id > 2) { 73 | //i.e result is processed 74 | return resultData; 75 | } else { 76 | await new Promise(resolve => setTimeout(resolve, 2000)); 77 | } 78 | } catch (error) { 79 | console.error(`Error polling Judge0 API for token ${token}`, error); 80 | await new Promise(resolve => setTimeout(resolve, 2000)); 81 | } 82 | } 83 | } 84 | 85 | async function startWorker() { 86 | try { 87 | await redisClient.connect(); 88 | console.log("Worker connected to Redis."); 89 | 90 | while (true) { 91 | try { 92 | const data = await redisClient.brPop("submissions", 0); 93 | if (data) { 94 | await processSubmission(data.element); 95 | } 96 | } catch (err) { 97 | console.error("Error processing submission:", err); 98 | } 99 | } 100 | } catch (err) { 101 | console.log(err); 102 | } 103 | } 104 | 105 | function getLanguageId(language: string): number { 106 | const languageMap: { [key: string]: number } = { 107 | "javascript": 63, 108 | "python": 71, 109 | "cpp": 54, 110 | "java": 62 111 | }; 112 | return languageMap[language.toLowerCase()]; 113 | } 114 | 115 | startWorker().catch(console.error); 116 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # System Design 3 | 4 | ![System Design](SystemDesign.png) 5 | 6 | # 🎉 Collaborative Code Editor 7 | 8 | Welcome to the Collaborative Code Editor (CCE) repository! This application allows multiple users to collaboratively write and edit code in real-time, leveraging the power of WebSockets. Users can sign up or log in with a unique username, create or join rooms with unique room IDs, and interact with other users through real-time chat and code editing. Each room supports multiple users who can collaboratively write code, select the programming language, submit their code for execution, and view the results together. The backend handles code execution using the Judge0 API and Redis for task queuing and pub/sub functionality. This makes CCE an ideal platform for collaborative coding sessions, educational environments, and coding competitions. 9 | 10 | ## 🌟 Features 11 | - **🔒 User Authentication**: Sign up and log in with unique usernames. 12 | - **🛏️ Room Management**: Create or join rooms with unique IDs. 13 | - **📝 Real-time Collaboration**: Code and chat in real-time with other users in the room. 14 | - **🌐 Multi-language support**: Choose the programming language for your code, supports 4 languages (python,javascript,java,c++) 15 | - **🚀 Code Submission and Execution**: Submit code and view results using the Judge0 API. 16 | - **🔄 Real-time Updates**: Automatic updates for code changes, language selection, user activity, and chat messages. 17 | 18 | ## 🛠️ Tech Stack 19 | - **Frontend**: Vite, React, TypeScript 20 | - **Backend**: Express, HTTP for WebSockets, MongoDB 21 | - **Queue and Pub/Sub System**: Redis 22 | 23 | ## 📦 Setup Guide 24 | 25 | ### Manual Setup 26 | 1. **Clone the repository**: 27 | ```sh 28 | git clone https://github.com/yourusername/collaborative-code-editor.git 29 | cd collaborative-code-editor 30 | ``` 31 | 32 | 2. **Install dependencies for each service**: 33 | ```sh 34 | # Server 35 | cd server 36 | npm install 37 | 38 | # Worker 39 | cd ../worker 40 | npm install 41 | 42 | # Client 43 | cd ../client 44 | npm install 45 | ``` 46 | 47 | 3. **Create a `.env` file** in each folder and configure your environment variables: (Refer the .env.example file) 48 | ```env 49 | # Example Client .env file 50 | VITE_REACT_APP_SERVER_URL = 51 | # Example Server .env file 52 | MONGO_URL = 53 | REDIS_URL = 54 | # Example Worker .env file 55 | //judge0 api key 56 | X_RAPID_API_KEY = 57 | REDIS_URL = 58 | 59 | 60 | 4. **Start redis locally** 61 | ```sh 62 | # In separate terminal 63 | # Using Docker 64 | docker run -p 6379:6379 --name redis-server -d redis 65 | ``` 66 | 67 | 5. **Start each service**: 68 | ```sh 69 | # Start Server 70 | cd server 71 | npm run start 72 | 73 | # Start Worker 74 | cd ../worker 75 | npm run start 76 | 77 | # Start Client 78 | cd ../client 79 | npm run dev 80 | ``` 81 | 82 | ### Docker Setup 83 | 1. **Navigate to the root directory**: 84 | ```sh 85 | cd path/to/collaborative-code-editor 86 | ``` 87 | 88 | 2. **Create a `.env` file** in each folder and configure your environment variables: (Refer the .env.example file) 89 | ```env 90 | # Example Client .env file 91 | VITE_REACT_APP_SERVER_URL = 92 | # Example Server .env file 93 | MONGO_URL = 94 | REDIS_URL = 95 | # Example Worker .env file 96 | //judge0 api key 97 | X_RAPID_API_KEY = 98 | REDIS_URL = 99 | 100 | 3. **Build and run the stack of containers**: 101 | ```sh 102 | docker-compose up --build 103 | ``` 104 | 105 | ### Troubleshooting 106 | - If you encounter an issue with `bcrypt` related to an invalid ELF header, perform the following steps: 107 | ```sh 108 | # Enter the server container using bin/bash 109 | docker exec -it server_container_name /bin/bash 110 | 111 | # Inside the container, reinstall bcrypt 112 | npm install bcrypt 113 | ``` 114 | 115 | 116 | ## 🚀 How It Works 117 | 1. **Sign Up / Log In**: Users sign up or log in with a unique username. 118 | 2. **Create / Join Room**: Users can create a new room with a unique room ID or join an existing room. 119 | 3. **WebSocket Connection**: Upon joining a room, a WebSocket connection is established. 120 | 4. **User Mapping**: On the server, the username is mapped to the WebSocket connection and added to the room's users array. 121 | 5. **Code Editor**: Users can write and edit code in a collaborative editor with a language selection option. 122 | 6. **Submit Code**: When a user submits code, it is pushed to a Redis queue. 123 | 7. **Worker Processing**: A worker listens to the queue, processes the code using the Judge0 API, and publishes the result to Redis. 124 | 8. **Result Broadcasting**: The main server subscribes to the result channel and broadcasts the result to all clients in the room. 125 | 126 | ## 🎨 Room Structure 127 | - **Name**: The name of the room. 128 | - **Room ID**: A unique identifier for the room. 129 | - **Users**: An array of user objects `{username, wss}`. 130 | - **Code**: The current code in the editor. 131 | - **Chats**: An array of chat objects `{username, message}`. 132 | - **Language**: The selected programming language. 133 | - **Result**: The result of the last code execution. 134 | 135 | ## 📬 Contributing 136 | We welcome contributions to the Collaborative Code Editor! To contribute: 137 | 1. Fork the repository. 138 | 2. Create a new branch (`git checkout -b feature/your-feature-name`). 139 | 3. Make your changes. 140 | 4. Commit your changes (`git commit -m 'Add some feature'`). 141 | 5. Push to the branch (`git push origin feature/your-feature-name`). 142 | 6. Open a Pull Request. 143 | 144 | Please ensure your code follows our coding standards and includes tests where applicable. 145 | 146 | ## 📄 License 147 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details. 148 | -------------------------------------------------------------------------------- /Server/src/utils/roomManager.ts: -------------------------------------------------------------------------------- 1 | import { WebSocket } from 'ws' 2 | 3 | interface room { 4 | name: string, 5 | roomId: string, 6 | users: Array<{ 7 | username: string, 8 | ws: WebSocket 9 | }>, 10 | code: string, 11 | chats: Array<{ 12 | username: string, 13 | message: string 14 | }>, 15 | language: string, 16 | result: string 17 | } 18 | 19 | import { createClient } from 'redis' 20 | 21 | export class RoomManager { 22 | private static instance : RoomManager; 23 | private rooms : room[] = []; 24 | 25 | private constructor(){ 26 | 27 | } 28 | 29 | public static getInstance(): RoomManager { 30 | if (!RoomManager.instance) { 31 | RoomManager.instance = new RoomManager(); 32 | } 33 | return RoomManager.instance; 34 | } 35 | 36 | public create(roomData : any){ 37 | 38 | const { roomName, roomId} = roomData 39 | 40 | const newRoom: room = { 41 | name: roomName, 42 | roomId: roomId, 43 | users: [], 44 | code: "", 45 | chats: [], 46 | language: "python", 47 | result: "" 48 | }; 49 | 50 | this.rooms.push(newRoom) 51 | 52 | } 53 | 54 | public handleUserJoined(message:any, ws : WebSocket) { 55 | const { roomId, username } = message; 56 | if(username==="") return; 57 | // Find the room based on roomId 58 | const room = this.rooms.find(room => room.roomId === roomId); 59 | if (!room) { 60 | const notFoundMessage = JSON.stringify({ 61 | Title : "Not-found" 62 | }) 63 | ws.send(notFoundMessage) 64 | return; 65 | } 66 | 67 | 68 | // Check if the user is already in the room 69 | const existingUserIndex = room.users.findIndex(user => user.username === username); 70 | if (existingUserIndex !== -1) { 71 | // Update the existing user's WebSocket connection 72 | room.users[existingUserIndex].ws = ws; 73 | 74 | // Send room info to the existing user 75 | const roomInfoMessage = JSON.stringify({ 76 | Title: "Room-Info", 77 | roomId, 78 | roomName: room.name, 79 | users: room.users.map(user => user.username), 80 | code: room.code, 81 | chats: room.chats, 82 | language: room.language, 83 | result: room.result 84 | }); 85 | ws.send(roomInfoMessage); 86 | return; 87 | } 88 | 89 | // Add the user to the room 90 | room.users.push({ username, ws }); 91 | 92 | // Send a message to all other users in the room about the new user 93 | const newUserMessage = JSON.stringify({ 94 | Title: "New-User", 95 | username 96 | }); 97 | 98 | room.users.forEach(user => { 99 | if (user.ws !== ws && user.ws.readyState === WebSocket.OPEN) { 100 | user.ws.send(newUserMessage); 101 | } 102 | }); 103 | 104 | // Send room info to the newly joined user 105 | const roomInfoMessage = JSON.stringify({ 106 | Title: "Room-Info", 107 | roomId, 108 | roomName: room.name, 109 | users: room.users.map(user => user.username), 110 | code: room.code, 111 | chats: room.chats, 112 | language: room.language, 113 | result: room.result 114 | }); 115 | ws.send(roomInfoMessage); 116 | } 117 | 118 | public handleUserLeft(message: any) { 119 | const { roomId, username } = message; 120 | if(username==="") return; 121 | // Find the room based on roomId 122 | const room = this.rooms.find(room => room.roomId === roomId); 123 | if (!room) { 124 | return; 125 | } 126 | 127 | // Remove the user from the room 128 | room.users = room.users.filter(user => user.username !== username); 129 | 130 | if(room.users.length===0){ 131 | this.rooms = this.rooms.filter(room => room.roomId !== roomId) 132 | return; 133 | } 134 | 135 | // Notify remaining users in the room 136 | const userLeftMessage = JSON.stringify({ 137 | Title: "User-left", 138 | username, 139 | users: room.users.map(user => user.username) 140 | }); 141 | 142 | room.users.forEach(user => { 143 | if (user.ws.readyState === WebSocket.OPEN) { 144 | user.ws.send(userLeftMessage); 145 | } 146 | }); 147 | } 148 | 149 | public handleNewChat(message:any){ 150 | const {roomId, username, chat} = message 151 | const room = this.rooms.find(room => room.roomId === roomId); 152 | if (!room) { 153 | return; 154 | } 155 | room.chats.push({username,message:chat}) 156 | const newChatMessage = JSON.stringify({ 157 | Title: "New-chat", 158 | username, 159 | chat 160 | }) 161 | room.users.forEach(user => { 162 | if (user.ws.readyState === WebSocket.OPEN) { 163 | user.ws.send(newChatMessage); 164 | } 165 | }); 166 | } 167 | 168 | public handleLangChange(message:any){ 169 | const { roomId, lang } = message 170 | const room = this.rooms.find(room => room.roomId === roomId); 171 | if (!room) { 172 | return; 173 | } 174 | room.language = lang; 175 | const langChangeMessage = { 176 | Title:"lang-change", 177 | lang 178 | } 179 | room.users.forEach(user => { 180 | if (user.ws.readyState === WebSocket.OPEN) { 181 | user.ws.send(JSON.stringify(langChangeMessage)); 182 | } 183 | }); 184 | } 185 | 186 | public handleCodeChange(message:any){ 187 | const { roomId, code } = message 188 | const room = this.rooms.find(room => room.roomId === roomId); 189 | if (!room) { 190 | return; 191 | } 192 | room.code=code 193 | const CodeChangeMessage = { 194 | Title:"Code-change", 195 | code 196 | } 197 | room.users.forEach(user => { 198 | if (user.ws.readyState === WebSocket.OPEN) { 199 | user.ws.send(JSON.stringify(CodeChangeMessage)); 200 | } 201 | }); 202 | } 203 | 204 | public async handleSubmitted(message:any , ws : WebSocket) { 205 | 206 | if(process.env.REDIS_URL === "No-Url-provided" || !process.env.REDIS_URL){ 207 | const resultMessage = { 208 | Title: "No-worker" 209 | } 210 | ws.send(JSON.stringify(resultMessage)) 211 | return; 212 | } 213 | 214 | const redisClient = createClient({ 215 | url: process.env.REDIS_URL 216 | }); 217 | 218 | const redisClientSubscribing = createClient({ 219 | url: process.env.REDIS_URL 220 | }); 221 | 222 | 223 | redisClient.connect().catch(err=>{console.log(err)}) 224 | redisClientSubscribing.connect().catch(err=>{console.log(err)}) 225 | 226 | const {roomId} = message 227 | const room = this.rooms.find(room => room.roomId === roomId); 228 | if (!room) { 229 | return; 230 | } 231 | 232 | const SubmitClickedMessage = { 233 | Title:"Submit-clicked" 234 | } 235 | 236 | room.users.forEach(user => { 237 | if (user.ws.readyState === WebSocket.OPEN) { 238 | user.ws.send(JSON.stringify(SubmitClickedMessage)); 239 | } 240 | }); 241 | 242 | 243 | 244 | //push the message into submissions queue 245 | await redisClient.lPush("submissions",JSON.stringify(message)) 246 | 247 | 248 | //subscribe to the roomId 249 | redisClientSubscribing.subscribe(roomId, (result) => { 250 | redisClientSubscribing.unsubscribe(roomId) 251 | // Parse the result received from the subscription 252 | if(result==="error"){ 253 | const msg = { 254 | Title : "error" 255 | } 256 | room.users.forEach(user => { 257 | if (user.ws.readyState === WebSocket.OPEN) { 258 | user.ws.send(JSON.stringify(msg)); 259 | } 260 | }); 261 | } 262 | else{ 263 | const parsedResult = JSON.parse(result); 264 | // Create a new JSON object containing the required fields 265 | const resultMessage = { 266 | Title: "Result", 267 | stdout: parsedResult.stdout, 268 | stderr: parsedResult.stderr, 269 | status: parsedResult.status.description, 270 | compile_output: parsedResult.compile_output 271 | }; 272 | 273 | // Send the resultMessageString to each user in the room 274 | room.users.forEach(user => { 275 | if (user.ws.readyState === WebSocket.OPEN) { 276 | user.ws.send(JSON.stringify(resultMessage)); 277 | } 278 | }); 279 | } 280 | }); 281 | } 282 | 283 | } -------------------------------------------------------------------------------- /Server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "./src", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Worker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "./src", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Client/src/pages/room.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams, useNavigate } from 'react-router-dom' 3 | import {useUserStore} from '../store' 4 | import { Box, Button, Flex, IconButton, Select, useDisclosure, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, useBreakpointValue, Spinner } from '@chakra-ui/react'; 5 | import { HamburgerIcon, ChatIcon } from '@chakra-ui/icons'; 6 | import CodeMirror from '@uiw/react-codemirror'; 7 | import { javascript } from '@codemirror/lang-javascript'; 8 | import { cpp } from '@codemirror/lang-cpp'; 9 | import { java } from '@codemirror/lang-java'; 10 | import { python } from '@codemirror/lang-python'; 11 | import Members from '../components/members'; 12 | import Chats from '../components/chats'; 13 | import ResultModal from '../components/result'; 14 | import toast, { Toaster } from 'react-hot-toast'; 15 | import { useUser } from '@clerk/clerk-react'; 16 | 17 | 18 | type chat = 19 | { 20 | username:string, 21 | message:string 22 | } 23 | 24 | const Room: React.FC = () => { 25 | const {roomId} = useParams(); 26 | const Navigate = useNavigate() 27 | const { setRoomID } = useUserStore(); 28 | const {user} = useUser(); 29 | const username = user?.username 30 | const [roomName,setRoomName] = useState("") 31 | const [users, setUsers] = useState([]) 32 | const [chats,setChats] = useState([]) 33 | const [result,setResult] = useState(null) 34 | const [language,setLanguage] = useState("") 35 | const [code,setCode] = useState("") 36 | const [socket, setSocket] = useState(null); 37 | const [submitClicked, setSubmitClicked] = useState(false); 38 | const [isModalOpen, setIsModalOpen] = useState(false); 39 | 40 | //fire socket events on lang change, chat , code change, new user is done, add button leave user and copy room id 41 | 42 | useEffect(()=>{ 43 | if(username==''){ 44 | Navigate("/auth") 45 | } 46 | let ws_url = "" 47 | if(import.meta.env.VITE_REACT_APP_SERVER_URL === "://localhost:8080"){ 48 | ws_url = `ws${import.meta.env.VITE_REACT_APP_SERVER_URL}` 49 | } 50 | else{ 51 | ws_url = `wss${import.meta.env.VITE_REACT_APP_SERVER_URL}` 52 | } 53 | const newSocket = new WebSocket(ws_url); 54 | newSocket.onopen = () => { 55 | console.log('Connection established'); 56 | const msg = { 57 | Title : "User-joined", 58 | roomId, 59 | username 60 | } 61 | newSocket.send(JSON.stringify(msg)) 62 | } 63 | newSocket.onmessage = (message) => { 64 | const parsedMessage = JSON.parse(message.data); 65 | if (parsedMessage.Title === "Room-Info") { 66 | setUsers(parsedMessage.users); 67 | setCode(parsedMessage.code); 68 | setLanguage(parsedMessage.language); 69 | setResult(parsedMessage.result); 70 | setChats(parsedMessage.chats); 71 | setRoomName(parsedMessage.roomName) 72 | } 73 | else if(parsedMessage.Title === "Not-found"){ 74 | alert("No room found") 75 | Navigate("/join") 76 | } 77 | else if (parsedMessage.Title === "New-User") { 78 | toast.success(`${parsedMessage.username} joined` ) 79 | setUsers(prevUsers => [...prevUsers, parsedMessage.username]); 80 | } 81 | else if(parsedMessage.Title === "User-left"){ 82 | toast.error(`${parsedMessage.username} left`) 83 | setUsers(parsedMessage.users) 84 | } 85 | else if(parsedMessage.Title === "New-chat"){ 86 | const {username, chat} = parsedMessage 87 | setChats((prevChats) => { 88 | // Create a new chat object 89 | const newChat = { username, message:chat }; 90 | 91 | // Return the updated chats array 92 | return [...prevChats, newChat]; 93 | }); 94 | } 95 | else if(parsedMessage.Title === "lang-change"){ 96 | const { lang } = parsedMessage 97 | setLanguage(lang) 98 | } 99 | else if(parsedMessage.Title === "Code-change"){ 100 | const { code } = parsedMessage 101 | setCode(code) 102 | } 103 | else if(parsedMessage.Title === "Submit-clicked"){ 104 | setSubmitClicked(true); 105 | } 106 | else if(parsedMessage.Title === "Result"){ 107 | setResult(parsedMessage) 108 | setSubmitClicked(false) 109 | setIsModalOpen(true); 110 | } 111 | else if(parsedMessage.Title === "No-worker"){ 112 | setSubmitClicked(false) 113 | alert("Code cannot be processed now cause it requires a continiously running worker service whcih is expensive 😅😅, if you want to, you can clone the repo and run worker process locally!!") 114 | } 115 | else if(parsedMessage.Title === "error"){ 116 | setSubmitClicked(false) 117 | alert("Error processing code as of now, retry or check code for errors!") 118 | } 119 | } 120 | setSocket(newSocket) 121 | return () => newSocket.close(); 122 | },[]) 123 | 124 | const { isOpen: isLeftOpen, onOpen: onLeftOpen, onClose: onLeftClose } = useDisclosure(); 125 | const { isOpen: isRightOpen, onOpen: onRightOpen, onClose: onRightClose } = useDisclosure(); 126 | const isLargeScreen = useBreakpointValue({ base: false, md: true }); 127 | 128 | 129 | 130 | 131 | const getLanguageExtension = (lang:string) => { 132 | switch (lang) { 133 | case 'javascript': 134 | return javascript({ jsx: true }); 135 | case 'java': 136 | return java(); 137 | case 'cpp': 138 | return cpp(); 139 | case 'python': 140 | return python(); 141 | default: 142 | return javascript({ jsx: true }); 143 | } 144 | }; 145 | 146 | const onLeave = () => { 147 | //fire websocket event 148 | if(users.length==1){ 149 | const confirmation = window.confirm( 150 | "You are the last user in this room. If you leave, all data will be deleted. Do you want to proceed?" 151 | ); 152 | if(!confirmation) return; 153 | } 154 | const msg = { 155 | Title : "User-left", 156 | roomId, 157 | username 158 | } 159 | socket?.send(JSON.stringify(msg)) 160 | setRoomID("") 161 | Navigate("/join") 162 | } 163 | 164 | const handleCodeChange = (val:string) => { 165 | //fire websocket event 166 | const msg = { 167 | Title: "Code-change", 168 | roomId, 169 | code:val 170 | } 171 | socket?.send(JSON.stringify(msg)) 172 | setCode(val) 173 | } 174 | 175 | const handleLangChange = (val:string) => { 176 | //fire websocket event 177 | const msg = { 178 | Title: "lang-change", 179 | roomId, 180 | lang:val 181 | } 182 | socket?.send(JSON.stringify(msg)) 183 | } 184 | 185 | const onSubmit = () => { 186 | //make a ws req to server, send the code there, subscribe to the roomid on pub-sub 187 | const msg = { 188 | Title : "Submitted", 189 | roomId, 190 | language, 191 | code 192 | } 193 | socket?.send(JSON.stringify(msg)) 194 | } 195 | 196 | function addChat(message:string){ 197 | //fire websocket event 198 | const msg = { 199 | Title : "New-chat", 200 | roomId, 201 | username, 202 | chat:message 203 | } 204 | socket?.send(JSON.stringify(msg)) 205 | } 206 | 207 | 208 | return ( 209 | 210 | 211 | {/* Left Sidebar for large screens */} 212 | {isLargeScreen && ( 213 | 214 |
Members
215 | 216 |
217 | )} 218 | {/* Left Sidebar Drawer */} 219 | {!isLargeScreen && ( 220 | 221 | 222 | 223 | 224 |
Members
225 | 226 | 227 | 228 |
229 |
230 | )} 231 | 232 | {/* Main Content */} 233 | 234 | {/* Header Bar */} 235 | 236 | 237 | 238 | 239 | {result && } 240 | 241 | { result && {setIsModalOpen(false)}} resultMessage={result}/> } 242 | 243 | 244 | 250 | <> 251 | {submitClicked ? ( 252 | ) : 253 | ( 254 | )} 255 | 256 | 257 | 258 | 259 | {roomName} 260 | 261 | 262 | {/* Sidebar Icons */} 263 | {!isLargeScreen && ( 264 | <> 265 | } 267 | aria-label="Members" 268 | display={{ base: 'block', md: 'none' }} 269 | position="absolute" 270 | left="4" 271 | onClick={onLeftOpen} 272 | /> 273 | } 275 | aria-label="Chats" 276 | display={{ base: 'block', md: 'none' }} 277 | position="absolute" 278 | right="4" 279 | onClick={onRightOpen} 280 | /> 281 | 282 | )} 283 | 284 | 285 | {/* editor here */} 286 | {handleCodeChange(val)}} 289 | height={useBreakpointValue({ base: '80vh', lg:"70vh", md: '60vh', sm: '50vh' })} 290 | width={useBreakpointValue({ base: '100vh', lg:"90vh", md: '80vh', sm: '70vh' })} 291 | extensions={[getLanguageExtension(language)]} 292 | theme={"dark"} 293 | /> 294 | 295 | 296 | 297 | {/* Right Sidebar Drawer */} 298 | {!isLargeScreen && ( 299 | 300 | 301 | 302 | 303 |
Chats
304 | 305 | 306 | 307 |
308 |
309 | )} 310 | 311 | {/* Right Sidebar for large screens */} 312 | {isLargeScreen && ( 313 | 314 |
Chats
315 | 316 |
317 | )} 318 |
319 | ); 320 | } 321 | 322 | export default Room -------------------------------------------------------------------------------- /Worker/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "worker", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/node": "^20.12.12", 13 | "axios": "^1.7.2", 14 | "dotenv": "^16.4.5", 15 | "node-fetch": "^3.3.2", 16 | "redis": "^4.6.14" 17 | }, 18 | "devDependencies": { 19 | "typescript": "^5.4.5" 20 | } 21 | }, 22 | "node_modules/@redis/bloom": { 23 | "version": "1.2.0", 24 | "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", 25 | "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", 26 | "peerDependencies": { 27 | "@redis/client": "^1.0.0" 28 | } 29 | }, 30 | "node_modules/@redis/client": { 31 | "version": "1.5.16", 32 | "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.16.tgz", 33 | "integrity": "sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==", 34 | "dependencies": { 35 | "cluster-key-slot": "1.1.2", 36 | "generic-pool": "3.9.0", 37 | "yallist": "4.0.0" 38 | }, 39 | "engines": { 40 | "node": ">=14" 41 | } 42 | }, 43 | "node_modules/@redis/graph": { 44 | "version": "1.1.1", 45 | "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", 46 | "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", 47 | "peerDependencies": { 48 | "@redis/client": "^1.0.0" 49 | } 50 | }, 51 | "node_modules/@redis/json": { 52 | "version": "1.0.6", 53 | "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", 54 | "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", 55 | "peerDependencies": { 56 | "@redis/client": "^1.0.0" 57 | } 58 | }, 59 | "node_modules/@redis/search": { 60 | "version": "1.1.6", 61 | "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", 62 | "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", 63 | "peerDependencies": { 64 | "@redis/client": "^1.0.0" 65 | } 66 | }, 67 | "node_modules/@redis/time-series": { 68 | "version": "1.0.5", 69 | "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", 70 | "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", 71 | "peerDependencies": { 72 | "@redis/client": "^1.0.0" 73 | } 74 | }, 75 | "node_modules/@types/node": { 76 | "version": "20.12.12", 77 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", 78 | "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", 79 | "dependencies": { 80 | "undici-types": "~5.26.4" 81 | } 82 | }, 83 | "node_modules/asynckit": { 84 | "version": "0.4.0", 85 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 86 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 87 | }, 88 | "node_modules/axios": { 89 | "version": "1.7.2", 90 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", 91 | "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", 92 | "dependencies": { 93 | "follow-redirects": "^1.15.6", 94 | "form-data": "^4.0.0", 95 | "proxy-from-env": "^1.1.0" 96 | } 97 | }, 98 | "node_modules/cluster-key-slot": { 99 | "version": "1.1.2", 100 | "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", 101 | "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", 102 | "engines": { 103 | "node": ">=0.10.0" 104 | } 105 | }, 106 | "node_modules/combined-stream": { 107 | "version": "1.0.8", 108 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 109 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 110 | "dependencies": { 111 | "delayed-stream": "~1.0.0" 112 | }, 113 | "engines": { 114 | "node": ">= 0.8" 115 | } 116 | }, 117 | "node_modules/data-uri-to-buffer": { 118 | "version": "4.0.1", 119 | "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", 120 | "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", 121 | "engines": { 122 | "node": ">= 12" 123 | } 124 | }, 125 | "node_modules/delayed-stream": { 126 | "version": "1.0.0", 127 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 128 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 129 | "engines": { 130 | "node": ">=0.4.0" 131 | } 132 | }, 133 | "node_modules/dotenv": { 134 | "version": "16.4.5", 135 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", 136 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", 137 | "engines": { 138 | "node": ">=12" 139 | }, 140 | "funding": { 141 | "url": "https://dotenvx.com" 142 | } 143 | }, 144 | "node_modules/fetch-blob": { 145 | "version": "3.2.0", 146 | "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", 147 | "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", 148 | "funding": [ 149 | { 150 | "type": "github", 151 | "url": "https://github.com/sponsors/jimmywarting" 152 | }, 153 | { 154 | "type": "paypal", 155 | "url": "https://paypal.me/jimmywarting" 156 | } 157 | ], 158 | "dependencies": { 159 | "node-domexception": "^1.0.0", 160 | "web-streams-polyfill": "^3.0.3" 161 | }, 162 | "engines": { 163 | "node": "^12.20 || >= 14.13" 164 | } 165 | }, 166 | "node_modules/follow-redirects": { 167 | "version": "1.15.6", 168 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", 169 | "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", 170 | "funding": [ 171 | { 172 | "type": "individual", 173 | "url": "https://github.com/sponsors/RubenVerborgh" 174 | } 175 | ], 176 | "engines": { 177 | "node": ">=4.0" 178 | }, 179 | "peerDependenciesMeta": { 180 | "debug": { 181 | "optional": true 182 | } 183 | } 184 | }, 185 | "node_modules/form-data": { 186 | "version": "4.0.0", 187 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 188 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 189 | "dependencies": { 190 | "asynckit": "^0.4.0", 191 | "combined-stream": "^1.0.8", 192 | "mime-types": "^2.1.12" 193 | }, 194 | "engines": { 195 | "node": ">= 6" 196 | } 197 | }, 198 | "node_modules/formdata-polyfill": { 199 | "version": "4.0.10", 200 | "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", 201 | "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", 202 | "dependencies": { 203 | "fetch-blob": "^3.1.2" 204 | }, 205 | "engines": { 206 | "node": ">=12.20.0" 207 | } 208 | }, 209 | "node_modules/generic-pool": { 210 | "version": "3.9.0", 211 | "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", 212 | "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", 213 | "engines": { 214 | "node": ">= 4" 215 | } 216 | }, 217 | "node_modules/mime-db": { 218 | "version": "1.52.0", 219 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 220 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 221 | "engines": { 222 | "node": ">= 0.6" 223 | } 224 | }, 225 | "node_modules/mime-types": { 226 | "version": "2.1.35", 227 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 228 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 229 | "dependencies": { 230 | "mime-db": "1.52.0" 231 | }, 232 | "engines": { 233 | "node": ">= 0.6" 234 | } 235 | }, 236 | "node_modules/node-domexception": { 237 | "version": "1.0.0", 238 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 239 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 240 | "funding": [ 241 | { 242 | "type": "github", 243 | "url": "https://github.com/sponsors/jimmywarting" 244 | }, 245 | { 246 | "type": "github", 247 | "url": "https://paypal.me/jimmywarting" 248 | } 249 | ], 250 | "engines": { 251 | "node": ">=10.5.0" 252 | } 253 | }, 254 | "node_modules/node-fetch": { 255 | "version": "3.3.2", 256 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", 257 | "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", 258 | "dependencies": { 259 | "data-uri-to-buffer": "^4.0.0", 260 | "fetch-blob": "^3.1.4", 261 | "formdata-polyfill": "^4.0.10" 262 | }, 263 | "engines": { 264 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 265 | }, 266 | "funding": { 267 | "type": "opencollective", 268 | "url": "https://opencollective.com/node-fetch" 269 | } 270 | }, 271 | "node_modules/proxy-from-env": { 272 | "version": "1.1.0", 273 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 274 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 275 | }, 276 | "node_modules/redis": { 277 | "version": "4.6.14", 278 | "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.14.tgz", 279 | "integrity": "sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==", 280 | "dependencies": { 281 | "@redis/bloom": "1.2.0", 282 | "@redis/client": "1.5.16", 283 | "@redis/graph": "1.1.1", 284 | "@redis/json": "1.0.6", 285 | "@redis/search": "1.1.6", 286 | "@redis/time-series": "1.0.5" 287 | } 288 | }, 289 | "node_modules/typescript": { 290 | "version": "5.4.5", 291 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 292 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 293 | "dev": true, 294 | "bin": { 295 | "tsc": "bin/tsc", 296 | "tsserver": "bin/tsserver" 297 | }, 298 | "engines": { 299 | "node": ">=14.17" 300 | } 301 | }, 302 | "node_modules/undici-types": { 303 | "version": "5.26.5", 304 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 305 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 306 | }, 307 | "node_modules/web-streams-polyfill": { 308 | "version": "3.3.3", 309 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", 310 | "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", 311 | "engines": { 312 | "node": ">= 8" 313 | } 314 | }, 315 | "node_modules/yallist": { 316 | "version": "4.0.0", 317 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 318 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 319 | } 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /Server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "server", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/bcrypt": "^5.0.2", 13 | "@types/express": "^4.17.21", 14 | "@types/ws": "^8.5.10", 15 | "bcrypt": "^5.1.1", 16 | "dotenv": "^16.4.5", 17 | "express": "^4.19.2", 18 | "mongoose": "^8.4.0", 19 | "redis": "^4.6.14", 20 | "ws": "^8.17.0" 21 | }, 22 | "devDependencies": { 23 | "@types/bcrypt": "^5.0.2", 24 | "@types/cors": "^2.8.17", 25 | "cors": "^2.8.5", 26 | "typescript": "^5.4.5" 27 | } 28 | }, 29 | "node_modules/@mapbox/node-pre-gyp": { 30 | "version": "1.0.11", 31 | "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", 32 | "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", 33 | "dependencies": { 34 | "detect-libc": "^2.0.0", 35 | "https-proxy-agent": "^5.0.0", 36 | "make-dir": "^3.1.0", 37 | "node-fetch": "^2.6.7", 38 | "nopt": "^5.0.0", 39 | "npmlog": "^5.0.1", 40 | "rimraf": "^3.0.2", 41 | "semver": "^7.3.5", 42 | "tar": "^6.1.11" 43 | }, 44 | "bin": { 45 | "node-pre-gyp": "bin/node-pre-gyp" 46 | } 47 | }, 48 | "node_modules/@mongodb-js/saslprep": { 49 | "version": "1.1.7", 50 | "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", 51 | "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", 52 | "dependencies": { 53 | "sparse-bitfield": "^3.0.3" 54 | } 55 | }, 56 | "node_modules/@redis/bloom": { 57 | "version": "1.2.0", 58 | "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", 59 | "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", 60 | "peerDependencies": { 61 | "@redis/client": "^1.0.0" 62 | } 63 | }, 64 | "node_modules/@redis/client": { 65 | "version": "1.5.16", 66 | "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.16.tgz", 67 | "integrity": "sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==", 68 | "dependencies": { 69 | "cluster-key-slot": "1.1.2", 70 | "generic-pool": "3.9.0", 71 | "yallist": "4.0.0" 72 | }, 73 | "engines": { 74 | "node": ">=14" 75 | } 76 | }, 77 | "node_modules/@redis/graph": { 78 | "version": "1.1.1", 79 | "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", 80 | "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", 81 | "peerDependencies": { 82 | "@redis/client": "^1.0.0" 83 | } 84 | }, 85 | "node_modules/@redis/json": { 86 | "version": "1.0.6", 87 | "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", 88 | "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", 89 | "peerDependencies": { 90 | "@redis/client": "^1.0.0" 91 | } 92 | }, 93 | "node_modules/@redis/search": { 94 | "version": "1.1.6", 95 | "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", 96 | "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", 97 | "peerDependencies": { 98 | "@redis/client": "^1.0.0" 99 | } 100 | }, 101 | "node_modules/@redis/time-series": { 102 | "version": "1.0.5", 103 | "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", 104 | "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", 105 | "peerDependencies": { 106 | "@redis/client": "^1.0.0" 107 | } 108 | }, 109 | "node_modules/@types/bcrypt": { 110 | "version": "5.0.2", 111 | "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", 112 | "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", 113 | "dev": true, 114 | "dependencies": { 115 | "@types/node": "*" 116 | } 117 | }, 118 | "node_modules/@types/body-parser": { 119 | "version": "1.19.5", 120 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", 121 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", 122 | "dependencies": { 123 | "@types/connect": "*", 124 | "@types/node": "*" 125 | } 126 | }, 127 | "node_modules/@types/connect": { 128 | "version": "3.4.38", 129 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", 130 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", 131 | "dependencies": { 132 | "@types/node": "*" 133 | } 134 | }, 135 | "node_modules/@types/cors": { 136 | "version": "2.8.17", 137 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 138 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 139 | "dev": true, 140 | "dependencies": { 141 | "@types/node": "*" 142 | } 143 | }, 144 | "node_modules/@types/express": { 145 | "version": "4.17.21", 146 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", 147 | "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", 148 | "dependencies": { 149 | "@types/body-parser": "*", 150 | "@types/express-serve-static-core": "^4.17.33", 151 | "@types/qs": "*", 152 | "@types/serve-static": "*" 153 | } 154 | }, 155 | "node_modules/@types/express-serve-static-core": { 156 | "version": "4.19.0", 157 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", 158 | "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", 159 | "dependencies": { 160 | "@types/node": "*", 161 | "@types/qs": "*", 162 | "@types/range-parser": "*", 163 | "@types/send": "*" 164 | } 165 | }, 166 | "node_modules/@types/http-errors": { 167 | "version": "2.0.4", 168 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", 169 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" 170 | }, 171 | "node_modules/@types/mime": { 172 | "version": "1.3.5", 173 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", 174 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" 175 | }, 176 | "node_modules/@types/node": { 177 | "version": "20.12.11", 178 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", 179 | "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", 180 | "dependencies": { 181 | "undici-types": "~5.26.4" 182 | } 183 | }, 184 | "node_modules/@types/qs": { 185 | "version": "6.9.15", 186 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", 187 | "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" 188 | }, 189 | "node_modules/@types/range-parser": { 190 | "version": "1.2.7", 191 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", 192 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" 193 | }, 194 | "node_modules/@types/send": { 195 | "version": "0.17.4", 196 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", 197 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", 198 | "dependencies": { 199 | "@types/mime": "^1", 200 | "@types/node": "*" 201 | } 202 | }, 203 | "node_modules/@types/serve-static": { 204 | "version": "1.15.7", 205 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", 206 | "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", 207 | "dependencies": { 208 | "@types/http-errors": "*", 209 | "@types/node": "*", 210 | "@types/send": "*" 211 | } 212 | }, 213 | "node_modules/@types/webidl-conversions": { 214 | "version": "7.0.3", 215 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", 216 | "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" 217 | }, 218 | "node_modules/@types/whatwg-url": { 219 | "version": "11.0.5", 220 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", 221 | "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", 222 | "dependencies": { 223 | "@types/webidl-conversions": "*" 224 | } 225 | }, 226 | "node_modules/@types/ws": { 227 | "version": "8.5.10", 228 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", 229 | "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", 230 | "dependencies": { 231 | "@types/node": "*" 232 | } 233 | }, 234 | "node_modules/abbrev": { 235 | "version": "1.1.1", 236 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 237 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 238 | }, 239 | "node_modules/accepts": { 240 | "version": "1.3.8", 241 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 242 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 243 | "dependencies": { 244 | "mime-types": "~2.1.34", 245 | "negotiator": "0.6.3" 246 | }, 247 | "engines": { 248 | "node": ">= 0.6" 249 | } 250 | }, 251 | "node_modules/agent-base": { 252 | "version": "6.0.2", 253 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 254 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 255 | "dependencies": { 256 | "debug": "4" 257 | }, 258 | "engines": { 259 | "node": ">= 6.0.0" 260 | } 261 | }, 262 | "node_modules/agent-base/node_modules/debug": { 263 | "version": "4.3.5", 264 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 265 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 266 | "dependencies": { 267 | "ms": "2.1.2" 268 | }, 269 | "engines": { 270 | "node": ">=6.0" 271 | }, 272 | "peerDependenciesMeta": { 273 | "supports-color": { 274 | "optional": true 275 | } 276 | } 277 | }, 278 | "node_modules/agent-base/node_modules/ms": { 279 | "version": "2.1.2", 280 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 281 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 282 | }, 283 | "node_modules/ansi-regex": { 284 | "version": "5.0.1", 285 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 286 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 287 | "engines": { 288 | "node": ">=8" 289 | } 290 | }, 291 | "node_modules/aproba": { 292 | "version": "2.0.0", 293 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", 294 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" 295 | }, 296 | "node_modules/are-we-there-yet": { 297 | "version": "2.0.0", 298 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", 299 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", 300 | "deprecated": "This package is no longer supported.", 301 | "dependencies": { 302 | "delegates": "^1.0.0", 303 | "readable-stream": "^3.6.0" 304 | }, 305 | "engines": { 306 | "node": ">=10" 307 | } 308 | }, 309 | "node_modules/array-flatten": { 310 | "version": "1.1.1", 311 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 312 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 313 | }, 314 | "node_modules/balanced-match": { 315 | "version": "1.0.2", 316 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 317 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 318 | }, 319 | "node_modules/bcrypt": { 320 | "version": "5.1.1", 321 | "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", 322 | "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", 323 | "hasInstallScript": true, 324 | "dependencies": { 325 | "@mapbox/node-pre-gyp": "^1.0.11", 326 | "node-addon-api": "^5.0.0" 327 | }, 328 | "engines": { 329 | "node": ">= 10.0.0" 330 | } 331 | }, 332 | "node_modules/body-parser": { 333 | "version": "1.20.2", 334 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 335 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 336 | "dependencies": { 337 | "bytes": "3.1.2", 338 | "content-type": "~1.0.5", 339 | "debug": "2.6.9", 340 | "depd": "2.0.0", 341 | "destroy": "1.2.0", 342 | "http-errors": "2.0.0", 343 | "iconv-lite": "0.4.24", 344 | "on-finished": "2.4.1", 345 | "qs": "6.11.0", 346 | "raw-body": "2.5.2", 347 | "type-is": "~1.6.18", 348 | "unpipe": "1.0.0" 349 | }, 350 | "engines": { 351 | "node": ">= 0.8", 352 | "npm": "1.2.8000 || >= 1.4.16" 353 | } 354 | }, 355 | "node_modules/brace-expansion": { 356 | "version": "1.1.11", 357 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 358 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 359 | "dependencies": { 360 | "balanced-match": "^1.0.0", 361 | "concat-map": "0.0.1" 362 | } 363 | }, 364 | "node_modules/bson": { 365 | "version": "6.7.0", 366 | "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", 367 | "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", 368 | "engines": { 369 | "node": ">=16.20.1" 370 | } 371 | }, 372 | "node_modules/bytes": { 373 | "version": "3.1.2", 374 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 375 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 376 | "engines": { 377 | "node": ">= 0.8" 378 | } 379 | }, 380 | "node_modules/call-bind": { 381 | "version": "1.0.7", 382 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 383 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 384 | "dependencies": { 385 | "es-define-property": "^1.0.0", 386 | "es-errors": "^1.3.0", 387 | "function-bind": "^1.1.2", 388 | "get-intrinsic": "^1.2.4", 389 | "set-function-length": "^1.2.1" 390 | }, 391 | "engines": { 392 | "node": ">= 0.4" 393 | }, 394 | "funding": { 395 | "url": "https://github.com/sponsors/ljharb" 396 | } 397 | }, 398 | "node_modules/chownr": { 399 | "version": "2.0.0", 400 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 401 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", 402 | "engines": { 403 | "node": ">=10" 404 | } 405 | }, 406 | "node_modules/cluster-key-slot": { 407 | "version": "1.1.2", 408 | "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", 409 | "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", 410 | "engines": { 411 | "node": ">=0.10.0" 412 | } 413 | }, 414 | "node_modules/color-support": { 415 | "version": "1.1.3", 416 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", 417 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", 418 | "bin": { 419 | "color-support": "bin.js" 420 | } 421 | }, 422 | "node_modules/concat-map": { 423 | "version": "0.0.1", 424 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 425 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 426 | }, 427 | "node_modules/console-control-strings": { 428 | "version": "1.1.0", 429 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 430 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" 431 | }, 432 | "node_modules/content-disposition": { 433 | "version": "0.5.4", 434 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 435 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 436 | "dependencies": { 437 | "safe-buffer": "5.2.1" 438 | }, 439 | "engines": { 440 | "node": ">= 0.6" 441 | } 442 | }, 443 | "node_modules/content-type": { 444 | "version": "1.0.5", 445 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 446 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 447 | "engines": { 448 | "node": ">= 0.6" 449 | } 450 | }, 451 | "node_modules/cookie": { 452 | "version": "0.6.0", 453 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 454 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 455 | "engines": { 456 | "node": ">= 0.6" 457 | } 458 | }, 459 | "node_modules/cookie-signature": { 460 | "version": "1.0.6", 461 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 462 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 463 | }, 464 | "node_modules/cors": { 465 | "version": "2.8.5", 466 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 467 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 468 | "dev": true, 469 | "dependencies": { 470 | "object-assign": "^4", 471 | "vary": "^1" 472 | }, 473 | "engines": { 474 | "node": ">= 0.10" 475 | } 476 | }, 477 | "node_modules/debug": { 478 | "version": "2.6.9", 479 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 480 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 481 | "dependencies": { 482 | "ms": "2.0.0" 483 | } 484 | }, 485 | "node_modules/define-data-property": { 486 | "version": "1.1.4", 487 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 488 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 489 | "dependencies": { 490 | "es-define-property": "^1.0.0", 491 | "es-errors": "^1.3.0", 492 | "gopd": "^1.0.1" 493 | }, 494 | "engines": { 495 | "node": ">= 0.4" 496 | }, 497 | "funding": { 498 | "url": "https://github.com/sponsors/ljharb" 499 | } 500 | }, 501 | "node_modules/delegates": { 502 | "version": "1.0.0", 503 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 504 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" 505 | }, 506 | "node_modules/depd": { 507 | "version": "2.0.0", 508 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 509 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 510 | "engines": { 511 | "node": ">= 0.8" 512 | } 513 | }, 514 | "node_modules/destroy": { 515 | "version": "1.2.0", 516 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 517 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 518 | "engines": { 519 | "node": ">= 0.8", 520 | "npm": "1.2.8000 || >= 1.4.16" 521 | } 522 | }, 523 | "node_modules/detect-libc": { 524 | "version": "2.0.3", 525 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", 526 | "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", 527 | "engines": { 528 | "node": ">=8" 529 | } 530 | }, 531 | "node_modules/dotenv": { 532 | "version": "16.4.5", 533 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", 534 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", 535 | "engines": { 536 | "node": ">=12" 537 | }, 538 | "funding": { 539 | "url": "https://dotenvx.com" 540 | } 541 | }, 542 | "node_modules/ee-first": { 543 | "version": "1.1.1", 544 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 545 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 546 | }, 547 | "node_modules/emoji-regex": { 548 | "version": "8.0.0", 549 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 550 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 551 | }, 552 | "node_modules/encodeurl": { 553 | "version": "1.0.2", 554 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 555 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 556 | "engines": { 557 | "node": ">= 0.8" 558 | } 559 | }, 560 | "node_modules/es-define-property": { 561 | "version": "1.0.0", 562 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 563 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 564 | "dependencies": { 565 | "get-intrinsic": "^1.2.4" 566 | }, 567 | "engines": { 568 | "node": ">= 0.4" 569 | } 570 | }, 571 | "node_modules/es-errors": { 572 | "version": "1.3.0", 573 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 574 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 575 | "engines": { 576 | "node": ">= 0.4" 577 | } 578 | }, 579 | "node_modules/escape-html": { 580 | "version": "1.0.3", 581 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 582 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 583 | }, 584 | "node_modules/etag": { 585 | "version": "1.8.1", 586 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 587 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 588 | "engines": { 589 | "node": ">= 0.6" 590 | } 591 | }, 592 | "node_modules/express": { 593 | "version": "4.19.2", 594 | "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", 595 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 596 | "dependencies": { 597 | "accepts": "~1.3.8", 598 | "array-flatten": "1.1.1", 599 | "body-parser": "1.20.2", 600 | "content-disposition": "0.5.4", 601 | "content-type": "~1.0.4", 602 | "cookie": "0.6.0", 603 | "cookie-signature": "1.0.6", 604 | "debug": "2.6.9", 605 | "depd": "2.0.0", 606 | "encodeurl": "~1.0.2", 607 | "escape-html": "~1.0.3", 608 | "etag": "~1.8.1", 609 | "finalhandler": "1.2.0", 610 | "fresh": "0.5.2", 611 | "http-errors": "2.0.0", 612 | "merge-descriptors": "1.0.1", 613 | "methods": "~1.1.2", 614 | "on-finished": "2.4.1", 615 | "parseurl": "~1.3.3", 616 | "path-to-regexp": "0.1.7", 617 | "proxy-addr": "~2.0.7", 618 | "qs": "6.11.0", 619 | "range-parser": "~1.2.1", 620 | "safe-buffer": "5.2.1", 621 | "send": "0.18.0", 622 | "serve-static": "1.15.0", 623 | "setprototypeof": "1.2.0", 624 | "statuses": "2.0.1", 625 | "type-is": "~1.6.18", 626 | "utils-merge": "1.0.1", 627 | "vary": "~1.1.2" 628 | }, 629 | "engines": { 630 | "node": ">= 0.10.0" 631 | } 632 | }, 633 | "node_modules/finalhandler": { 634 | "version": "1.2.0", 635 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 636 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 637 | "dependencies": { 638 | "debug": "2.6.9", 639 | "encodeurl": "~1.0.2", 640 | "escape-html": "~1.0.3", 641 | "on-finished": "2.4.1", 642 | "parseurl": "~1.3.3", 643 | "statuses": "2.0.1", 644 | "unpipe": "~1.0.0" 645 | }, 646 | "engines": { 647 | "node": ">= 0.8" 648 | } 649 | }, 650 | "node_modules/forwarded": { 651 | "version": "0.2.0", 652 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 653 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 654 | "engines": { 655 | "node": ">= 0.6" 656 | } 657 | }, 658 | "node_modules/fresh": { 659 | "version": "0.5.2", 660 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 661 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 662 | "engines": { 663 | "node": ">= 0.6" 664 | } 665 | }, 666 | "node_modules/fs-minipass": { 667 | "version": "2.1.0", 668 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 669 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 670 | "dependencies": { 671 | "minipass": "^3.0.0" 672 | }, 673 | "engines": { 674 | "node": ">= 8" 675 | } 676 | }, 677 | "node_modules/fs-minipass/node_modules/minipass": { 678 | "version": "3.3.6", 679 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 680 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 681 | "dependencies": { 682 | "yallist": "^4.0.0" 683 | }, 684 | "engines": { 685 | "node": ">=8" 686 | } 687 | }, 688 | "node_modules/fs.realpath": { 689 | "version": "1.0.0", 690 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 691 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 692 | }, 693 | "node_modules/function-bind": { 694 | "version": "1.1.2", 695 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 696 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 697 | "funding": { 698 | "url": "https://github.com/sponsors/ljharb" 699 | } 700 | }, 701 | "node_modules/gauge": { 702 | "version": "3.0.2", 703 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", 704 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", 705 | "deprecated": "This package is no longer supported.", 706 | "dependencies": { 707 | "aproba": "^1.0.3 || ^2.0.0", 708 | "color-support": "^1.1.2", 709 | "console-control-strings": "^1.0.0", 710 | "has-unicode": "^2.0.1", 711 | "object-assign": "^4.1.1", 712 | "signal-exit": "^3.0.0", 713 | "string-width": "^4.2.3", 714 | "strip-ansi": "^6.0.1", 715 | "wide-align": "^1.1.2" 716 | }, 717 | "engines": { 718 | "node": ">=10" 719 | } 720 | }, 721 | "node_modules/generic-pool": { 722 | "version": "3.9.0", 723 | "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", 724 | "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", 725 | "engines": { 726 | "node": ">= 4" 727 | } 728 | }, 729 | "node_modules/get-intrinsic": { 730 | "version": "1.2.4", 731 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 732 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 733 | "dependencies": { 734 | "es-errors": "^1.3.0", 735 | "function-bind": "^1.1.2", 736 | "has-proto": "^1.0.1", 737 | "has-symbols": "^1.0.3", 738 | "hasown": "^2.0.0" 739 | }, 740 | "engines": { 741 | "node": ">= 0.4" 742 | }, 743 | "funding": { 744 | "url": "https://github.com/sponsors/ljharb" 745 | } 746 | }, 747 | "node_modules/glob": { 748 | "version": "7.2.3", 749 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 750 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 751 | "deprecated": "Glob versions prior to v9 are no longer supported", 752 | "dependencies": { 753 | "fs.realpath": "^1.0.0", 754 | "inflight": "^1.0.4", 755 | "inherits": "2", 756 | "minimatch": "^3.1.1", 757 | "once": "^1.3.0", 758 | "path-is-absolute": "^1.0.0" 759 | }, 760 | "engines": { 761 | "node": "*" 762 | }, 763 | "funding": { 764 | "url": "https://github.com/sponsors/isaacs" 765 | } 766 | }, 767 | "node_modules/gopd": { 768 | "version": "1.0.1", 769 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 770 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 771 | "dependencies": { 772 | "get-intrinsic": "^1.1.3" 773 | }, 774 | "funding": { 775 | "url": "https://github.com/sponsors/ljharb" 776 | } 777 | }, 778 | "node_modules/has-property-descriptors": { 779 | "version": "1.0.2", 780 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 781 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 782 | "dependencies": { 783 | "es-define-property": "^1.0.0" 784 | }, 785 | "funding": { 786 | "url": "https://github.com/sponsors/ljharb" 787 | } 788 | }, 789 | "node_modules/has-proto": { 790 | "version": "1.0.3", 791 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 792 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 793 | "engines": { 794 | "node": ">= 0.4" 795 | }, 796 | "funding": { 797 | "url": "https://github.com/sponsors/ljharb" 798 | } 799 | }, 800 | "node_modules/has-symbols": { 801 | "version": "1.0.3", 802 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 803 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 804 | "engines": { 805 | "node": ">= 0.4" 806 | }, 807 | "funding": { 808 | "url": "https://github.com/sponsors/ljharb" 809 | } 810 | }, 811 | "node_modules/has-unicode": { 812 | "version": "2.0.1", 813 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 814 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" 815 | }, 816 | "node_modules/hasown": { 817 | "version": "2.0.2", 818 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 819 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 820 | "dependencies": { 821 | "function-bind": "^1.1.2" 822 | }, 823 | "engines": { 824 | "node": ">= 0.4" 825 | } 826 | }, 827 | "node_modules/http-errors": { 828 | "version": "2.0.0", 829 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 830 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 831 | "dependencies": { 832 | "depd": "2.0.0", 833 | "inherits": "2.0.4", 834 | "setprototypeof": "1.2.0", 835 | "statuses": "2.0.1", 836 | "toidentifier": "1.0.1" 837 | }, 838 | "engines": { 839 | "node": ">= 0.8" 840 | } 841 | }, 842 | "node_modules/https-proxy-agent": { 843 | "version": "5.0.1", 844 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 845 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 846 | "dependencies": { 847 | "agent-base": "6", 848 | "debug": "4" 849 | }, 850 | "engines": { 851 | "node": ">= 6" 852 | } 853 | }, 854 | "node_modules/https-proxy-agent/node_modules/debug": { 855 | "version": "4.3.5", 856 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 857 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 858 | "dependencies": { 859 | "ms": "2.1.2" 860 | }, 861 | "engines": { 862 | "node": ">=6.0" 863 | }, 864 | "peerDependenciesMeta": { 865 | "supports-color": { 866 | "optional": true 867 | } 868 | } 869 | }, 870 | "node_modules/https-proxy-agent/node_modules/ms": { 871 | "version": "2.1.2", 872 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 873 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 874 | }, 875 | "node_modules/iconv-lite": { 876 | "version": "0.4.24", 877 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 878 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 879 | "dependencies": { 880 | "safer-buffer": ">= 2.1.2 < 3" 881 | }, 882 | "engines": { 883 | "node": ">=0.10.0" 884 | } 885 | }, 886 | "node_modules/inflight": { 887 | "version": "1.0.6", 888 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 889 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 890 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 891 | "dependencies": { 892 | "once": "^1.3.0", 893 | "wrappy": "1" 894 | } 895 | }, 896 | "node_modules/inherits": { 897 | "version": "2.0.4", 898 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 899 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 900 | }, 901 | "node_modules/ipaddr.js": { 902 | "version": "1.9.1", 903 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 904 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 905 | "engines": { 906 | "node": ">= 0.10" 907 | } 908 | }, 909 | "node_modules/is-fullwidth-code-point": { 910 | "version": "3.0.0", 911 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 912 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 913 | "engines": { 914 | "node": ">=8" 915 | } 916 | }, 917 | "node_modules/kareem": { 918 | "version": "2.6.3", 919 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", 920 | "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", 921 | "engines": { 922 | "node": ">=12.0.0" 923 | } 924 | }, 925 | "node_modules/make-dir": { 926 | "version": "3.1.0", 927 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 928 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 929 | "dependencies": { 930 | "semver": "^6.0.0" 931 | }, 932 | "engines": { 933 | "node": ">=8" 934 | }, 935 | "funding": { 936 | "url": "https://github.com/sponsors/sindresorhus" 937 | } 938 | }, 939 | "node_modules/make-dir/node_modules/semver": { 940 | "version": "6.3.1", 941 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 942 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 943 | "bin": { 944 | "semver": "bin/semver.js" 945 | } 946 | }, 947 | "node_modules/media-typer": { 948 | "version": "0.3.0", 949 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 950 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 951 | "engines": { 952 | "node": ">= 0.6" 953 | } 954 | }, 955 | "node_modules/memory-pager": { 956 | "version": "1.5.0", 957 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 958 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" 959 | }, 960 | "node_modules/merge-descriptors": { 961 | "version": "1.0.1", 962 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 963 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 964 | }, 965 | "node_modules/methods": { 966 | "version": "1.1.2", 967 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 968 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 969 | "engines": { 970 | "node": ">= 0.6" 971 | } 972 | }, 973 | "node_modules/mime": { 974 | "version": "1.6.0", 975 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 976 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 977 | "bin": { 978 | "mime": "cli.js" 979 | }, 980 | "engines": { 981 | "node": ">=4" 982 | } 983 | }, 984 | "node_modules/mime-db": { 985 | "version": "1.52.0", 986 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 987 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 988 | "engines": { 989 | "node": ">= 0.6" 990 | } 991 | }, 992 | "node_modules/mime-types": { 993 | "version": "2.1.35", 994 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 995 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 996 | "dependencies": { 997 | "mime-db": "1.52.0" 998 | }, 999 | "engines": { 1000 | "node": ">= 0.6" 1001 | } 1002 | }, 1003 | "node_modules/minimatch": { 1004 | "version": "3.1.2", 1005 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1006 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1007 | "dependencies": { 1008 | "brace-expansion": "^1.1.7" 1009 | }, 1010 | "engines": { 1011 | "node": "*" 1012 | } 1013 | }, 1014 | "node_modules/minipass": { 1015 | "version": "5.0.0", 1016 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", 1017 | "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", 1018 | "engines": { 1019 | "node": ">=8" 1020 | } 1021 | }, 1022 | "node_modules/minizlib": { 1023 | "version": "2.1.2", 1024 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 1025 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 1026 | "dependencies": { 1027 | "minipass": "^3.0.0", 1028 | "yallist": "^4.0.0" 1029 | }, 1030 | "engines": { 1031 | "node": ">= 8" 1032 | } 1033 | }, 1034 | "node_modules/minizlib/node_modules/minipass": { 1035 | "version": "3.3.6", 1036 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 1037 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 1038 | "dependencies": { 1039 | "yallist": "^4.0.0" 1040 | }, 1041 | "engines": { 1042 | "node": ">=8" 1043 | } 1044 | }, 1045 | "node_modules/mkdirp": { 1046 | "version": "1.0.4", 1047 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 1048 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 1049 | "bin": { 1050 | "mkdirp": "bin/cmd.js" 1051 | }, 1052 | "engines": { 1053 | "node": ">=10" 1054 | } 1055 | }, 1056 | "node_modules/mongodb": { 1057 | "version": "6.6.2", 1058 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", 1059 | "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", 1060 | "dependencies": { 1061 | "@mongodb-js/saslprep": "^1.1.5", 1062 | "bson": "^6.7.0", 1063 | "mongodb-connection-string-url": "^3.0.0" 1064 | }, 1065 | "engines": { 1066 | "node": ">=16.20.1" 1067 | }, 1068 | "peerDependencies": { 1069 | "@aws-sdk/credential-providers": "^3.188.0", 1070 | "@mongodb-js/zstd": "^1.1.0", 1071 | "gcp-metadata": "^5.2.0", 1072 | "kerberos": "^2.0.1", 1073 | "mongodb-client-encryption": ">=6.0.0 <7", 1074 | "snappy": "^7.2.2", 1075 | "socks": "^2.7.1" 1076 | }, 1077 | "peerDependenciesMeta": { 1078 | "@aws-sdk/credential-providers": { 1079 | "optional": true 1080 | }, 1081 | "@mongodb-js/zstd": { 1082 | "optional": true 1083 | }, 1084 | "gcp-metadata": { 1085 | "optional": true 1086 | }, 1087 | "kerberos": { 1088 | "optional": true 1089 | }, 1090 | "mongodb-client-encryption": { 1091 | "optional": true 1092 | }, 1093 | "snappy": { 1094 | "optional": true 1095 | }, 1096 | "socks": { 1097 | "optional": true 1098 | } 1099 | } 1100 | }, 1101 | "node_modules/mongodb-connection-string-url": { 1102 | "version": "3.0.1", 1103 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", 1104 | "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", 1105 | "dependencies": { 1106 | "@types/whatwg-url": "^11.0.2", 1107 | "whatwg-url": "^13.0.0" 1108 | } 1109 | }, 1110 | "node_modules/mongoose": { 1111 | "version": "8.4.0", 1112 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.0.tgz", 1113 | "integrity": "sha512-fgqRMwVEP1qgRYfh+tUe2YBBFnPO35FIg2lfFH+w9IhRGg1/ataWGIqvf/MjwM29cZ60D5vSnqtN2b8Qp0sOZA==", 1114 | "dependencies": { 1115 | "bson": "^6.7.0", 1116 | "kareem": "2.6.3", 1117 | "mongodb": "6.6.2", 1118 | "mpath": "0.9.0", 1119 | "mquery": "5.0.0", 1120 | "ms": "2.1.3", 1121 | "sift": "17.1.3" 1122 | }, 1123 | "engines": { 1124 | "node": ">=16.20.1" 1125 | }, 1126 | "funding": { 1127 | "type": "opencollective", 1128 | "url": "https://opencollective.com/mongoose" 1129 | } 1130 | }, 1131 | "node_modules/mongoose/node_modules/ms": { 1132 | "version": "2.1.3", 1133 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1134 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1135 | }, 1136 | "node_modules/mpath": { 1137 | "version": "0.9.0", 1138 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", 1139 | "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", 1140 | "engines": { 1141 | "node": ">=4.0.0" 1142 | } 1143 | }, 1144 | "node_modules/mquery": { 1145 | "version": "5.0.0", 1146 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", 1147 | "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", 1148 | "dependencies": { 1149 | "debug": "4.x" 1150 | }, 1151 | "engines": { 1152 | "node": ">=14.0.0" 1153 | } 1154 | }, 1155 | "node_modules/mquery/node_modules/debug": { 1156 | "version": "4.3.4", 1157 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1158 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1159 | "dependencies": { 1160 | "ms": "2.1.2" 1161 | }, 1162 | "engines": { 1163 | "node": ">=6.0" 1164 | }, 1165 | "peerDependenciesMeta": { 1166 | "supports-color": { 1167 | "optional": true 1168 | } 1169 | } 1170 | }, 1171 | "node_modules/mquery/node_modules/ms": { 1172 | "version": "2.1.2", 1173 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1174 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1175 | }, 1176 | "node_modules/ms": { 1177 | "version": "2.0.0", 1178 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1179 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1180 | }, 1181 | "node_modules/negotiator": { 1182 | "version": "0.6.3", 1183 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1184 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1185 | "engines": { 1186 | "node": ">= 0.6" 1187 | } 1188 | }, 1189 | "node_modules/node-addon-api": { 1190 | "version": "5.1.0", 1191 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", 1192 | "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" 1193 | }, 1194 | "node_modules/node-fetch": { 1195 | "version": "2.7.0", 1196 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1197 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1198 | "dependencies": { 1199 | "whatwg-url": "^5.0.0" 1200 | }, 1201 | "engines": { 1202 | "node": "4.x || >=6.0.0" 1203 | }, 1204 | "peerDependencies": { 1205 | "encoding": "^0.1.0" 1206 | }, 1207 | "peerDependenciesMeta": { 1208 | "encoding": { 1209 | "optional": true 1210 | } 1211 | } 1212 | }, 1213 | "node_modules/node-fetch/node_modules/tr46": { 1214 | "version": "0.0.3", 1215 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1216 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1217 | }, 1218 | "node_modules/node-fetch/node_modules/webidl-conversions": { 1219 | "version": "3.0.1", 1220 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1221 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1222 | }, 1223 | "node_modules/node-fetch/node_modules/whatwg-url": { 1224 | "version": "5.0.0", 1225 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1226 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1227 | "dependencies": { 1228 | "tr46": "~0.0.3", 1229 | "webidl-conversions": "^3.0.0" 1230 | } 1231 | }, 1232 | "node_modules/nopt": { 1233 | "version": "5.0.0", 1234 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", 1235 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", 1236 | "dependencies": { 1237 | "abbrev": "1" 1238 | }, 1239 | "bin": { 1240 | "nopt": "bin/nopt.js" 1241 | }, 1242 | "engines": { 1243 | "node": ">=6" 1244 | } 1245 | }, 1246 | "node_modules/npmlog": { 1247 | "version": "5.0.1", 1248 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", 1249 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", 1250 | "deprecated": "This package is no longer supported.", 1251 | "dependencies": { 1252 | "are-we-there-yet": "^2.0.0", 1253 | "console-control-strings": "^1.1.0", 1254 | "gauge": "^3.0.0", 1255 | "set-blocking": "^2.0.0" 1256 | } 1257 | }, 1258 | "node_modules/object-assign": { 1259 | "version": "4.1.1", 1260 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1261 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1262 | "engines": { 1263 | "node": ">=0.10.0" 1264 | } 1265 | }, 1266 | "node_modules/object-inspect": { 1267 | "version": "1.13.1", 1268 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", 1269 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", 1270 | "funding": { 1271 | "url": "https://github.com/sponsors/ljharb" 1272 | } 1273 | }, 1274 | "node_modules/on-finished": { 1275 | "version": "2.4.1", 1276 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1277 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1278 | "dependencies": { 1279 | "ee-first": "1.1.1" 1280 | }, 1281 | "engines": { 1282 | "node": ">= 0.8" 1283 | } 1284 | }, 1285 | "node_modules/once": { 1286 | "version": "1.4.0", 1287 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1288 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1289 | "dependencies": { 1290 | "wrappy": "1" 1291 | } 1292 | }, 1293 | "node_modules/parseurl": { 1294 | "version": "1.3.3", 1295 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1296 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1297 | "engines": { 1298 | "node": ">= 0.8" 1299 | } 1300 | }, 1301 | "node_modules/path-is-absolute": { 1302 | "version": "1.0.1", 1303 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1304 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1305 | "engines": { 1306 | "node": ">=0.10.0" 1307 | } 1308 | }, 1309 | "node_modules/path-to-regexp": { 1310 | "version": "0.1.7", 1311 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1312 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 1313 | }, 1314 | "node_modules/proxy-addr": { 1315 | "version": "2.0.7", 1316 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1317 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1318 | "dependencies": { 1319 | "forwarded": "0.2.0", 1320 | "ipaddr.js": "1.9.1" 1321 | }, 1322 | "engines": { 1323 | "node": ">= 0.10" 1324 | } 1325 | }, 1326 | "node_modules/punycode": { 1327 | "version": "2.3.1", 1328 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1329 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1330 | "engines": { 1331 | "node": ">=6" 1332 | } 1333 | }, 1334 | "node_modules/qs": { 1335 | "version": "6.11.0", 1336 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1337 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1338 | "dependencies": { 1339 | "side-channel": "^1.0.4" 1340 | }, 1341 | "engines": { 1342 | "node": ">=0.6" 1343 | }, 1344 | "funding": { 1345 | "url": "https://github.com/sponsors/ljharb" 1346 | } 1347 | }, 1348 | "node_modules/range-parser": { 1349 | "version": "1.2.1", 1350 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1351 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1352 | "engines": { 1353 | "node": ">= 0.6" 1354 | } 1355 | }, 1356 | "node_modules/raw-body": { 1357 | "version": "2.5.2", 1358 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 1359 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 1360 | "dependencies": { 1361 | "bytes": "3.1.2", 1362 | "http-errors": "2.0.0", 1363 | "iconv-lite": "0.4.24", 1364 | "unpipe": "1.0.0" 1365 | }, 1366 | "engines": { 1367 | "node": ">= 0.8" 1368 | } 1369 | }, 1370 | "node_modules/readable-stream": { 1371 | "version": "3.6.2", 1372 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1373 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1374 | "dependencies": { 1375 | "inherits": "^2.0.3", 1376 | "string_decoder": "^1.1.1", 1377 | "util-deprecate": "^1.0.1" 1378 | }, 1379 | "engines": { 1380 | "node": ">= 6" 1381 | } 1382 | }, 1383 | "node_modules/redis": { 1384 | "version": "4.6.14", 1385 | "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.14.tgz", 1386 | "integrity": "sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==", 1387 | "dependencies": { 1388 | "@redis/bloom": "1.2.0", 1389 | "@redis/client": "1.5.16", 1390 | "@redis/graph": "1.1.1", 1391 | "@redis/json": "1.0.6", 1392 | "@redis/search": "1.1.6", 1393 | "@redis/time-series": "1.0.5" 1394 | } 1395 | }, 1396 | "node_modules/rimraf": { 1397 | "version": "3.0.2", 1398 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1399 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1400 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 1401 | "dependencies": { 1402 | "glob": "^7.1.3" 1403 | }, 1404 | "bin": { 1405 | "rimraf": "bin.js" 1406 | }, 1407 | "funding": { 1408 | "url": "https://github.com/sponsors/isaacs" 1409 | } 1410 | }, 1411 | "node_modules/safe-buffer": { 1412 | "version": "5.2.1", 1413 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1414 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1415 | "funding": [ 1416 | { 1417 | "type": "github", 1418 | "url": "https://github.com/sponsors/feross" 1419 | }, 1420 | { 1421 | "type": "patreon", 1422 | "url": "https://www.patreon.com/feross" 1423 | }, 1424 | { 1425 | "type": "consulting", 1426 | "url": "https://feross.org/support" 1427 | } 1428 | ] 1429 | }, 1430 | "node_modules/safer-buffer": { 1431 | "version": "2.1.2", 1432 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1433 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1434 | }, 1435 | "node_modules/semver": { 1436 | "version": "7.6.3", 1437 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 1438 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 1439 | "bin": { 1440 | "semver": "bin/semver.js" 1441 | }, 1442 | "engines": { 1443 | "node": ">=10" 1444 | } 1445 | }, 1446 | "node_modules/send": { 1447 | "version": "0.18.0", 1448 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1449 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1450 | "dependencies": { 1451 | "debug": "2.6.9", 1452 | "depd": "2.0.0", 1453 | "destroy": "1.2.0", 1454 | "encodeurl": "~1.0.2", 1455 | "escape-html": "~1.0.3", 1456 | "etag": "~1.8.1", 1457 | "fresh": "0.5.2", 1458 | "http-errors": "2.0.0", 1459 | "mime": "1.6.0", 1460 | "ms": "2.1.3", 1461 | "on-finished": "2.4.1", 1462 | "range-parser": "~1.2.1", 1463 | "statuses": "2.0.1" 1464 | }, 1465 | "engines": { 1466 | "node": ">= 0.8.0" 1467 | } 1468 | }, 1469 | "node_modules/send/node_modules/ms": { 1470 | "version": "2.1.3", 1471 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1472 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1473 | }, 1474 | "node_modules/serve-static": { 1475 | "version": "1.15.0", 1476 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1477 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1478 | "dependencies": { 1479 | "encodeurl": "~1.0.2", 1480 | "escape-html": "~1.0.3", 1481 | "parseurl": "~1.3.3", 1482 | "send": "0.18.0" 1483 | }, 1484 | "engines": { 1485 | "node": ">= 0.8.0" 1486 | } 1487 | }, 1488 | "node_modules/set-blocking": { 1489 | "version": "2.0.0", 1490 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1491 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 1492 | }, 1493 | "node_modules/set-function-length": { 1494 | "version": "1.2.2", 1495 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 1496 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 1497 | "dependencies": { 1498 | "define-data-property": "^1.1.4", 1499 | "es-errors": "^1.3.0", 1500 | "function-bind": "^1.1.2", 1501 | "get-intrinsic": "^1.2.4", 1502 | "gopd": "^1.0.1", 1503 | "has-property-descriptors": "^1.0.2" 1504 | }, 1505 | "engines": { 1506 | "node": ">= 0.4" 1507 | } 1508 | }, 1509 | "node_modules/setprototypeof": { 1510 | "version": "1.2.0", 1511 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1512 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1513 | }, 1514 | "node_modules/side-channel": { 1515 | "version": "1.0.6", 1516 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 1517 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 1518 | "dependencies": { 1519 | "call-bind": "^1.0.7", 1520 | "es-errors": "^1.3.0", 1521 | "get-intrinsic": "^1.2.4", 1522 | "object-inspect": "^1.13.1" 1523 | }, 1524 | "engines": { 1525 | "node": ">= 0.4" 1526 | }, 1527 | "funding": { 1528 | "url": "https://github.com/sponsors/ljharb" 1529 | } 1530 | }, 1531 | "node_modules/sift": { 1532 | "version": "17.1.3", 1533 | "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", 1534 | "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" 1535 | }, 1536 | "node_modules/signal-exit": { 1537 | "version": "3.0.7", 1538 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1539 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 1540 | }, 1541 | "node_modules/sparse-bitfield": { 1542 | "version": "3.0.3", 1543 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1544 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 1545 | "dependencies": { 1546 | "memory-pager": "^1.0.2" 1547 | } 1548 | }, 1549 | "node_modules/statuses": { 1550 | "version": "2.0.1", 1551 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1552 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1553 | "engines": { 1554 | "node": ">= 0.8" 1555 | } 1556 | }, 1557 | "node_modules/string_decoder": { 1558 | "version": "1.3.0", 1559 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1560 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1561 | "dependencies": { 1562 | "safe-buffer": "~5.2.0" 1563 | } 1564 | }, 1565 | "node_modules/string-width": { 1566 | "version": "4.2.3", 1567 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1568 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1569 | "dependencies": { 1570 | "emoji-regex": "^8.0.0", 1571 | "is-fullwidth-code-point": "^3.0.0", 1572 | "strip-ansi": "^6.0.1" 1573 | }, 1574 | "engines": { 1575 | "node": ">=8" 1576 | } 1577 | }, 1578 | "node_modules/strip-ansi": { 1579 | "version": "6.0.1", 1580 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1581 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1582 | "dependencies": { 1583 | "ansi-regex": "^5.0.1" 1584 | }, 1585 | "engines": { 1586 | "node": ">=8" 1587 | } 1588 | }, 1589 | "node_modules/tar": { 1590 | "version": "6.2.1", 1591 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", 1592 | "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", 1593 | "dependencies": { 1594 | "chownr": "^2.0.0", 1595 | "fs-minipass": "^2.0.0", 1596 | "minipass": "^5.0.0", 1597 | "minizlib": "^2.1.1", 1598 | "mkdirp": "^1.0.3", 1599 | "yallist": "^4.0.0" 1600 | }, 1601 | "engines": { 1602 | "node": ">=10" 1603 | } 1604 | }, 1605 | "node_modules/toidentifier": { 1606 | "version": "1.0.1", 1607 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1608 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1609 | "engines": { 1610 | "node": ">=0.6" 1611 | } 1612 | }, 1613 | "node_modules/tr46": { 1614 | "version": "4.1.1", 1615 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", 1616 | "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", 1617 | "dependencies": { 1618 | "punycode": "^2.3.0" 1619 | }, 1620 | "engines": { 1621 | "node": ">=14" 1622 | } 1623 | }, 1624 | "node_modules/type-is": { 1625 | "version": "1.6.18", 1626 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1627 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1628 | "dependencies": { 1629 | "media-typer": "0.3.0", 1630 | "mime-types": "~2.1.24" 1631 | }, 1632 | "engines": { 1633 | "node": ">= 0.6" 1634 | } 1635 | }, 1636 | "node_modules/typescript": { 1637 | "version": "5.4.5", 1638 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 1639 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 1640 | "dev": true, 1641 | "bin": { 1642 | "tsc": "bin/tsc", 1643 | "tsserver": "bin/tsserver" 1644 | }, 1645 | "engines": { 1646 | "node": ">=14.17" 1647 | } 1648 | }, 1649 | "node_modules/undici-types": { 1650 | "version": "5.26.5", 1651 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1652 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1653 | }, 1654 | "node_modules/unpipe": { 1655 | "version": "1.0.0", 1656 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1657 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1658 | "engines": { 1659 | "node": ">= 0.8" 1660 | } 1661 | }, 1662 | "node_modules/util-deprecate": { 1663 | "version": "1.0.2", 1664 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1665 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1666 | }, 1667 | "node_modules/utils-merge": { 1668 | "version": "1.0.1", 1669 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1670 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1671 | "engines": { 1672 | "node": ">= 0.4.0" 1673 | } 1674 | }, 1675 | "node_modules/vary": { 1676 | "version": "1.1.2", 1677 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1678 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1679 | "engines": { 1680 | "node": ">= 0.8" 1681 | } 1682 | }, 1683 | "node_modules/webidl-conversions": { 1684 | "version": "7.0.0", 1685 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 1686 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 1687 | "engines": { 1688 | "node": ">=12" 1689 | } 1690 | }, 1691 | "node_modules/whatwg-url": { 1692 | "version": "13.0.0", 1693 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", 1694 | "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", 1695 | "dependencies": { 1696 | "tr46": "^4.1.1", 1697 | "webidl-conversions": "^7.0.0" 1698 | }, 1699 | "engines": { 1700 | "node": ">=16" 1701 | } 1702 | }, 1703 | "node_modules/wide-align": { 1704 | "version": "1.1.5", 1705 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 1706 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 1707 | "dependencies": { 1708 | "string-width": "^1.0.2 || 2 || 3 || 4" 1709 | } 1710 | }, 1711 | "node_modules/wrappy": { 1712 | "version": "1.0.2", 1713 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1714 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1715 | }, 1716 | "node_modules/ws": { 1717 | "version": "8.18.0", 1718 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 1719 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 1720 | "engines": { 1721 | "node": ">=10.0.0" 1722 | }, 1723 | "peerDependencies": { 1724 | "bufferutil": "^4.0.1", 1725 | "utf-8-validate": ">=5.0.2" 1726 | }, 1727 | "peerDependenciesMeta": { 1728 | "bufferutil": { 1729 | "optional": true 1730 | }, 1731 | "utf-8-validate": { 1732 | "optional": true 1733 | } 1734 | } 1735 | }, 1736 | "node_modules/yallist": { 1737 | "version": "4.0.0", 1738 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1739 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1740 | } 1741 | } 1742 | } 1743 | --------------------------------------------------------------------------------