diff --git a/.env.example b/.env.example index c0f190d..b6527f5 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,12 @@ MONGO_DB_PASSWORD=1234 REDIS_DOCKER_URL=redis://redis:6379 REDIS_LOCALHOST_URL=redis://localhost:6379 +SERVER_URL=http://localhost:3000 +WEBSOCKET_URL=ws://localhost:3000 + +ENCRYPTION_KEY_SECRET=encryption-key-secret #(32 bytes) +ENCRYPTION_KEY_IV=encryption-key-iv #(12 bytes) + SESSION_SECRET=session-secret SESSION_EXPIRES_IN=180d @@ -15,6 +21,8 @@ RECAPTCHA_SECRET=recaptcha-secret RECAPTCHA_SITE=recaptcha-site DISABLE_RECAPTCHA=false +EMAIL_PROVIDER=mailtrap-gmail + TELWARE_EMAIL=telware@gmail.com TELWARE_PASSWORD=telware-password GMAIL_HOST=smtp.gmail.com @@ -36,4 +44,18 @@ GITHUB_CLIENT_SECRET=githu-client-secret CROSS_PLATFORM_OAUTH_REDIRECT_URL=telware://telware.online/social-auth-loading FRONTEND_URL=localhost:5174 -GROUP_SIZE= 5 \ No newline at end of file +GROUP_SIZE= 5 + +FIREBASE_SERVICE_ACCOUNT='{ + "type": "service_account", + "project_id": "your-project-id", + "private_key_id": "your-private-key-id", + "private_key": "-----BEGIN PRIVATE KEY-----\\nYOUR_PRIVATE_KEY\\n-----END PRIVATE KEY-----\\n", + "client_email": "your-client-email@your-project.iam.gserviceaccount.com", + "client_id": "your-client-id", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/your-client-email%40your-project.iam.gserviceaccount.com" + "universe_domain": "googleapis.com" +}' diff --git a/.github/workflows/checkCodeCompilationForPeter.yaml b/.github/workflows/checkCodeCompilationForPeter.yaml new file mode 100644 index 0000000..f4e4d85 --- /dev/null +++ b/.github/workflows/checkCodeCompilationForPeter.yaml @@ -0,0 +1,39 @@ +name: Check That Code Compiles For Peter + +on: + push: + branches: + - develop + - main + + pull_request: + branches: + - main + - develop + + pull_request_target: + branches: + - main + - develop + +jobs: + check-code-compilation: + + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: setup node + uses: actions/setup-node@v2 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm install + + - name: Run Server + run: npm run build + + diff --git a/README.md b/README.md index b606303..d764491 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # telware backend + +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=TelwareSW_telware-backend&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=TelwareSW_telware-backend) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=TelwareSW_telware-backend&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=TelwareSW_telware-backend) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=TelwareSW_telware-backend&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=TelwareSW_telware-backend) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=TelwareSW_telware-backend&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=TelwareSW_telware-backend) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=TelwareSW_telware-backend&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=TelwareSW_telware-backend) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=TelwareSW_telware-backend&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=TelwareSW_telware-backend) + + Backend Repo for TelWare Messaging Platform diff --git a/docs/api/chat.swagger.ts b/docs/api/chat.swagger.ts index 06cbb38..177256e 100644 --- a/docs/api/chat.swagger.ts +++ b/docs/api/chat.swagger.ts @@ -61,42 +61,30 @@ * Role: * type: string * description: Role of the user in the chat (e.g., member, admin). - * _id: - * type: string - * description: Unique identifier for the membership entry. - * id: - * type: string - * description: Alias for `_id`. * type: * type: string * description: Type of the chat (e.g., "private", "group"). + * encryptionKey: + * type: string + * description: Key used for encrypting chat messages. + * initializationVector: + * type: string + * description: Initialization vector for encrypting chat messages. * isDeleted: * type: boolean * description: Indicates if the chat has been deleted. * chatType: * type: string * description: Specific type of the chat (e.g., "NormalChat"). - * __v: - * type: integer - * description: Version key. * numberOfMembers: * type: integer * description: Total number of members in the chat. - * id: - * type: string - * description: Alias for `_id`. * isMuted: * type: boolean * description: Indicates if the chat is muted. * draft: * type: string * description: Draft message saved for the chat. - * _id: - * type: string - * description: Unique identifier for the chat entry. - * id: - * type: string - * description: Alias for `_id`. * members: * type: array * items: @@ -136,9 +124,20 @@ * items: * type: string * description: IDs of users blocked by this member. - * id: + * unreadMessages: + * type: array + * items: + * type: object + * properties: + * chatId: + * type: string + * description: Unique identifier of the chat this message belongs to. + * unreadMessagesCount: * type: string - * description: Alias for `_id`. + * description: Number of unread messages in the chat. + * isMentioned: + * type: boolean + * description: Indicates if the user is mentioned in the chat. * lastMessages: * type: array * items: @@ -186,12 +185,6 @@ * type: string * format: date-time * description: Timestamp when the message was sent. - * __v: - * type: integer - * description: Version key. - * id: - * type: string - * description: Alias for `_id`. * 401: * description: User is not logged in or the request is invalid. * content: @@ -315,9 +308,9 @@ * name: page * required: false * schema: - * type: integer - * example: 1 - * description: The page number for paginated messages (default is 1). + * type: objectId + * example: "674cbbba97faf0d2e8a93846" + * description: The page starting after that messageId. * - in: query * name: limit * required: false @@ -325,6 +318,13 @@ * type: integer * example: 50 * description: The number of messages to retrieve per page (default is 100). + * - in: query + * name: timestamp + * required: false + * schema: + * type: Date + * example: 2024-12-01T19:37:56.399Z + * description: The timestamp to retrieve messages after. * responses: * 200: * description: Messages retrieved successfully. @@ -356,6 +356,12 @@ * contentType: * type: string * example: text + * media: + * type: string + * example: "media-file-name.jpg" + * isEdited: + * type: boolean + * example: false * isPinned: * type: boolean * example: false @@ -383,17 +389,8 @@ * items: * type: string * example: [] - * messageType: - * type: string - * example: private - * __v: - * type: integer - * example: 0 - * id: - * type: string - * example: "674cbbba97faf0d2e8a93846" * nextPage: - * type: integer + * type: objectId * example: 2 * 400: * description: Bad Request - Chat does not exist. @@ -1063,3 +1060,67 @@ * type: string * example: login first */ + +/** + * @swagger + * /chats/voice-calls/{chatId}: + * get: + * summary: Retrieve voice calls in a specific chat. + * description: Fetch all voice calls associated with a given chat ID. Access is restricted to chat members. + * tags: + * - Chat + * parameters: + * - in: path + * name: chatId + * required: true + * schema: + * type: string + * description: The ID of the chat whose voice calls are being retrieved. + * responses: + * 200: + * description: Voice calls retrieved successfully. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: success + * message: + * type: string + * example: voice calls retrieved successfully + * data: + * type: object + * properties: + * voiceCalls: + * type: array + * items: + * type: object + * 400: + * description: Missing required fields or invalid request. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: fail + * message: + * type: string + * example: missing required fields + * 403: + * description: Unauthorized request due to missing or invalid user authentication. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: fail + * message: + * type: string + * example: login first + */ diff --git a/docs/api/search.swagger.ts b/docs/api/search.swagger.ts index 363159d..53c0e8c 100644 --- a/docs/api/search.swagger.ts +++ b/docs/api/search.swagger.ts @@ -4,11 +4,10 @@ * name: Search * description: API for searching content across various spaces with optional filters and global search capabilities. */ - /** * @swagger - * /search-request: - * get: + * /search/search-request: + * post: * summary: Perform a search across specified spaces with optional filters. * tags: [Search] * description: Allows users to search within specific spaces like chats, channels, and groups, apply filters, and enable global search for a broader scope. @@ -155,6 +154,7 @@ * example: "An unexpected error occurred while processing the request." */ + /** * @swagger * tags: diff --git a/docs/api/sockets.swagger.ts b/docs/api/sockets.swagger.ts index af4a5e4..b031547 100644 --- a/docs/api/sockets.swagger.ts +++ b/docs/api/sockets.swagger.ts @@ -7,7 +7,7 @@ /** * @swagger - * /SEND-FORWARD-REPLY: + * SEND_MESSAGE: * post: * summary: Handle messaging functionality, including creating chats, replying, and forwarding messages. * tags: @@ -39,19 +39,15 @@ * type: string * nullable: true * description: ID of the parent message for reply or forward. - * isFirstTime: - * type: boolean - * description: Indicates whether the chat is being initiated for the first time. - * chatType: - * type: string - * enum: [group, direct, channel] - * description: Type of the chat. * isReply: * type: boolean * description: Indicates if the message is a reply. * isForward: * type: boolean * description: Indicates if the message is forwarded. + * isAnouncement: + * type: boolean + * description: Indicates if the message is an anouncement. * responses: * 200: * description: Message sent successfully. @@ -124,7 +120,7 @@ /** * @swagger - * /EDIT_MESSAGE: + * EDIT_MESSAGE_CLIENT: * patch: * summary: Edits an existing message in the chat. * description: Edits an existing message in the chat. It is not possible to edit forwarded messages. @@ -249,7 +245,7 @@ /** * @swagger - * /DELETE_MESSAGE: + * DELETE_MESSAGE_CLIENT: * delete: * summary: Deletes a Message. * description: Deletes a message from the chat based on the provided message ID. If the message is found, it is deleted from the chat. @@ -324,10 +320,10 @@ /** * @swagger - * /UPDATE_DRAFT_CLIENT: + * HANDLE_READ_MESSAGE: * post: - * summary: Updates a draft message - * description: Updates an existing draft message with new content. The updated draft is saved and the client is notified of the update. + * summary: Marks messages as read + * description: Updates the status of unread messages in a chat for the user. Marks them as read and notifies the sender. * tags: [Sockets] * requestBody: * required: true @@ -337,17 +333,13 @@ * type: object * required: * - chatId - * - content * properties: * chatId: * type: string - * description: The unique ID of the chat to update its draft. - * content: - * type: string - * description: The new content of the draft. + * description: The unique ID of the chat whose messages need to be marked as read. * responses: * 200: - * description: Draft updated successfully. + * description: Messages marked as read successfully. * content: * application/json: * schema: @@ -379,10 +371,10 @@ /** * @swagger - * /UPDATE_DRAFT_SERVER: + * UPDATE_DRAFT_CLIENT: * post: - * summary: Emits an event to update a draft message on the server - * description: Emits an event to update an existing draft message with new content on the server. The server processes the update and notifies the client of the status. + * summary: Updates a draft message + * description: Updates an existing draft message with new content. The updated draft is saved and the client is notified of the update. * tags: [Sockets] * requestBody: * required: true @@ -392,17 +384,17 @@ * type: object * required: * - chatId - * - draft + * - content * properties: * chatId: * type: string - * description: The unique ID of the chat whose draft is being updated. - * draft: + * description: The unique ID of the chat to update its draft. + * content: * type: string - * description: The new content of the draft message. + * description: The new content of the draft. * responses: * 200: - * description: Draft update event emitted successfully. + * description: Draft updated successfully. * content: * application/json: * schema: @@ -410,7 +402,7 @@ * properties: * success: * type: boolean - * description: Indicates if the event was successfully emitted. + * description: Indicates if the operation was successful. * message: * type: string * description: A message describing the result. @@ -429,9 +421,33 @@ * description: An error message describing the problem. * error: * type: string - * description: Details about the error (e.g., invalid input). - * 500: - * description: Internal server error occurred while emitting the event. + * description: Details about the error (e.g., missing fields). + */ + +/** + * @swagger + * CREATE_PRIVATE_CHAT: + * post: + * summary: "Create a new private chat" + * description: "This socket event allows a user to create a new private chat. It validates the user's existence and establishes a one-to-one chat." + * tags: + * - Sockets + * operationId: "createPrivateChat" + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * memberId: + * type: string + * description: "ID of the user to be added as the other member of the private chat." + * required: + * - memberId + * responses: + * 200: + * description: "Private chat created successfully." * content: * application/json: * schema: @@ -439,18 +455,48 @@ * properties: * success: * type: boolean - * description: Indicates if the operation was successful. + * example: true + * message: + * type: string + * example: "Chat created successfully." + * data: + * type: object + * properties: + * _id: + * type: string + * description: "ID of the newly created private chat." + * members: + * type: array + * items: + * type: object + * properties: + * user: + * type: string + * description: "ID of the user." + * Role: + * type: string + * description: "Role of the user in the private chat (e.g., 'member')." + * 400: + * description: "Invalid input or constraints violated." + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: false * message: * type: string - * description: An error message describing the result. + * example: "Failed to create the chat." * error: * type: string - * description: Details about the server error. + * description: "Error message describing the issue." */ /** * @swagger - * /CREATE_GROUP_CHANNEL: + * CREATE_GROUP_CHANNEL: * post: * summary: "Create a new group channel" * description: "This socket event allows a user to create a new group channel. It validates the user's login status, group size, and adds members with roles." @@ -537,7 +583,7 @@ /** * @swagger - * /DELETE_GROUP_CHANNEL_CLIENT: + * DELETE_GROUP_CHANNEL_CLIENT: * delete: * summary: "Delete a group or channel" * description: "This socket event allows the admin of a group channel to delete the group. All members will be informed about the deletion." @@ -593,7 +639,7 @@ /** * @swagger - * /LEAVE_GROUP_CHANNEL_CLIENT: + * LEAVE_GROUP_CHANNEL_CLIENT: * delete: * summary: "Leave a group channel" * description: "Allows a user to leave a group channel. Other members in the group will be notified of the member's departure." @@ -912,3 +958,89 @@ * type: string * example: This chat does not exist or has been deleted */ + +/** + * @swagger + * CREATE-CALL: + * post: + * summary: Initiates a new voice call. + * tags: [Sockets] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * chatId: + * type: string + * description: The unique ID of the chat where the voice call is started. + * targetId: + * type: string + * description: Used to when there is no chat between the two users. + * example: "98765" + */ + +/** + * @swagger + * JOIN-CALL: + * post: + * summary: Joins an existing voice call. + * tags: [Sockets] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * voiceCallId: + * type: string + * description: The ID of the voice call to join. + */ + +/** + * @swagger + * SIGNAL-SERVER: + * post: + * summary: Sends signaling data to a specific client. + * tags: [Sockets] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * type: + * type: string + * enum: [ICE, OFFER, ANSWER] + * description: The type of signaling message. + * targetId: + * type: string + * description: The ID of the target client. + * voiceCallId: + * type: string + * description: The ID of the voice call. + * data: + * type: object + * description: The actual signaling data. + */ + +/** + * @swagger + * LEAVE: + * post: + * summary: Leaves a voice call. + * tags: [Sockets] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * voiceCallId: + * type: string + * description: The ID of the voice call to leave. + */ diff --git a/docs/api/user.swagger.ts b/docs/api/user.swagger.ts index 461c91f..3cb2630 100644 --- a/docs/api/user.swagger.ts +++ b/docs/api/user.swagger.ts @@ -1416,3 +1416,71 @@ * - Session not found, you are not allowed here! * - You are not authorized to view these stories */ + +/** + * @swagger + * /users/fcm-token: + * patch: + * summary: Update the FCM token of the authenticated user + * description: This endpoint allows an authenticated user to update their Firebase Cloud Messaging (FCM) token. + * tags: + * - User + * security: + * - cookieAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * fcmToken: + * type: string + * description: The FCM token to be updated. + * example: "aslkdfadslfdaskf" + * required: + * - fcmToken + * responses: + * 201: + * description: FCM token updated successfully. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: "success" + * message: + * type: string + * example: "User fcm token updated successfully" + * data: + * type: object + * example: {} + * 404: + * description: No user exists with the given ID. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: "error" + * message: + * type: string + * example: "No User exists with this ID" + * 500: + * description: Internal server error. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: "error" + * message: + * type: string + * example: "Internal Server Error" + */ diff --git a/package-lock.json b/package-lock.json index a6d45e7..94ee706 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "license": "ISC", "dependencies": { "@faker-js/faker": "^9.0.3", - "axios": "^1.7.7", + "@google/generative-ai": "^0.21.0", + "@huggingface/inference": "^2.8.1", + "axios": "^1.7.9", "bcrypt": "^5.1.1", "body-parser": "^1.20.3", "connect-redis": "^7.1.1", @@ -21,6 +23,7 @@ "express-mongo-sanitize": "^2.2.0", "express-rate-limit": "^7.4.1", "express-session": "^1.18.1", + "firebase-admin": "^13.0.2", "helmet": "^8.0.0", "hpp": "^0.2.3", "jsonwebtoken": "^9.0.2", @@ -185,9 +188,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, "engines": { "node": ">=6.9.0" @@ -224,13 +227,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -251,19 +254,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", @@ -302,13 +292,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", - "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.1.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -319,9 +309,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -432,19 +422,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", @@ -513,12 +490,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -1105,12 +1082,11 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -1245,14 +1221,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1607,9 +1582,9 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz", - "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz", + "integrity": "sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", @@ -1831,16 +1806,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1858,9 +1833,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1908,530 +1883,408 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@esbuild/aix-ppc64": { + "node_modules/@esbuild/linux-x64": { "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", "cpu": [ - "ppc64" + "x64" ], "dev": true, "optional": true, "os": [ - "aix" + "linux" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=18" + "node": "*" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" + "node_modules/@faker-js/faker": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.3.0.tgz", + "integrity": "sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } ], "engines": { - "node": ">=18" + "node": ">=18.0.0", + "npm": ">=9.0.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/@fastify/busboy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.0.tgz", + "integrity": "sha512-yHmUtGwEbW6HsKpPqT140/L6GpHtquHogRLgtanJFep3UAfDkE0fQfC49U+F9irCAoJVlv3M7VSp4rrtO4LnfA==" + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/component": { + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.11.tgz", + "integrity": "sha512-eQbeCgPukLgsKD0Kw5wQgsMDX5LeoI1MIrziNDjmc6XDq5ZQnuUymANQgAb2wp1tSF9zDSXyxJmIUXaKgN58Ug==", + "dependencies": { + "@firebase/util": "1.10.2", + "tslib": "^2.1.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/@firebase/database": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.10.tgz", + "integrity": "sha512-sWp2g92u7xT4BojGbTXZ80iaSIaL6GAL0pwvM0CO/hb0nHSnABAqsH7AhnWGsGvXuEvbPr7blZylPaR9J+GSuQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.11", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.2", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/@firebase/database-compat": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.1.tgz", + "integrity": "sha512-IsFivOjdE1GrjTeKoBU/ZMenESKDXidFDzZzHBPQ/4P20ptGdrl3oLlWrV/QJqJ9lND4IidE3z4Xr5JyfUW1vg==", + "dependencies": { + "@firebase/component": "0.6.11", + "@firebase/database": "1.0.10", + "@firebase/database-types": "1.0.7", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.2", + "tslib": "^2.1.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "node_modules/@firebase/database-types": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.7.tgz", + "integrity": "sha512-I7zcLfJXrM0WM+ksFmFdAMdlq/DFmpeMNa+/GNsLyFo5u/lX5zzkPzGe3srVWqaBQBY5KprylDGxOsP6ETfL0A==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.10.2" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], + "node_modules/@firebase/util": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.2.tgz", + "integrity": "sha512-qnSHIoE9FK+HYnNhTI8q14evyqbc/vHRivfB4TgCIUOl4tosmKSQlp7ltymOlMP4xVIJTg5wrkfcZ60X4nUf7Q==", + "dependencies": { + "tslib": "^2.1.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, "engines": { - "node": ">=18" + "node": ">=14.0.0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, "engines": { - "node": ">=18" + "node": ">=14.0.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "optional": true, - "os": [ - "sunos" - ], "engines": { - "node": ">=18" + "node": ">=14.0.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=14" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", - "cpu": [ - "ia32" - ], - "dev": true, + "node_modules/@google-cloud/storage": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.14.0.tgz", + "integrity": "sha512-H41bPL2cMfSi4EEnFzKvg7XSb7T67ocSXrmF7MPjfgFB0L6CKGzfIYJheAZi1iqXjz6XaCT1OBf6HCG5vDBTOQ==", "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, "engines": { - "node": ">=18" + "node": ">=14" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@google-cloud/storage/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "bin": { + "mime": "cli.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=10.0.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": ">=18.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "optional": true, "dependencies": { - "type-fest": "^0.20.2" + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.10.0" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "optional": true, "dependencies": { - "argparse": "^2.0.1" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" }, "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" + "node_modules/@huggingface/inference": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.8.1.tgz", + "integrity": "sha512-EfsNtY9OR6JCNaUa5bZu2mrs48iqeTz0Gutwf+fU0Kypx33xFQB4DKMhp8u4Ee6qVbLbNWvTHuWwlppLQl4p4Q==", + "license": "MIT", + "dependencies": { + "@huggingface/tasks": "^0.12.9" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@faker-js/faker": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.2.0.tgz", - "integrity": "sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/fakerjs" - } - ], - "engines": { - "node": ">=18.0.0", - "npm": ">=9.0.0" - } + "node_modules/@huggingface/tasks": { + "version": "0.12.30", + "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.12.30.tgz", + "integrity": "sha512-A1ITdxbEzx9L8wKR8pF7swyrTLxWNDFIGDLUWInxvks2ruQ8PLRBZe8r0EcjC3CDdtlj9jV1V4cgV35K/iy3GQ==" }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", @@ -2822,9 +2675,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -2869,6 +2722,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@jsdevtools/ono": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", @@ -2994,6 +2857,15 @@ "node": ">=12.4.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "optional": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -3006,6 +2878,70 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "optional": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "optional": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "optional": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "optional": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "optional": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "optional": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "optional": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "optional": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "optional": true + }, "node_modules/@redis/bloom": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", @@ -3077,54 +3013,54 @@ "hasInstallScript": true }, "node_modules/@shikijs/core": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.23.1.tgz", - "integrity": "sha512-NuOVgwcHgVC6jBVH5V7iblziw6iQbWWHrj5IlZI3Fqu2yx9awH7OIQkXIcsHsUmY19ckwSgUMgrqExEyP5A0TA==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.24.3.tgz", + "integrity": "sha512-VRcf4GYUIkxIchGM9DrapRcxtgojg4IWKUtX5EtW+4PJiGzF2xQqZSv27PJt+WLc18KT3CNLpNWow9JYV5n+Rg==", "dev": true, "dependencies": { - "@shikijs/engine-javascript": "1.23.1", - "@shikijs/engine-oniguruma": "1.23.1", - "@shikijs/types": "1.23.1", - "@shikijs/vscode-textmate": "^9.3.0", + "@shikijs/engine-javascript": "1.24.3", + "@shikijs/engine-oniguruma": "1.24.3", + "@shikijs/types": "1.24.3", + "@shikijs/vscode-textmate": "^9.3.1", "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.3" + "hast-util-to-html": "^9.0.4" } }, "node_modules/@shikijs/engine-javascript": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.23.1.tgz", - "integrity": "sha512-i/LdEwT5k3FVu07SiApRFwRcSJs5QM9+tod5vYCPig1Ywi8GR30zcujbxGQFJHwYD7A5BUqagi8o5KS+LEVgBg==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.24.3.tgz", + "integrity": "sha512-De8tNLvYjeK6V0Gb47jIH2M+OKkw+lWnSV1j3HVDFMlNIglmVcTMG2fASc29W0zuFbfEEwKjO8Fe4KYSO6Ce3w==", "dev": true, "dependencies": { - "@shikijs/types": "1.23.1", - "@shikijs/vscode-textmate": "^9.3.0", - "oniguruma-to-es": "0.4.1" + "@shikijs/types": "1.24.3", + "@shikijs/vscode-textmate": "^9.3.1", + "oniguruma-to-es": "0.8.0" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.23.1.tgz", - "integrity": "sha512-KQ+lgeJJ5m2ISbUZudLR1qHeH3MnSs2mjFg7bnencgs5jDVPeJ2NVDJ3N5ZHbcTsOIh0qIueyAJnwg7lg7kwXQ==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.3.tgz", + "integrity": "sha512-iNnx950gs/5Nk+zrp1LuF+S+L7SKEhn8k9eXgFYPGhVshKppsYwRmW8tpmAMvILIMSDfrgqZ0w+3xWVQB//1Xw==", "dev": true, "dependencies": { - "@shikijs/types": "1.23.1", - "@shikijs/vscode-textmate": "^9.3.0" + "@shikijs/types": "1.24.3", + "@shikijs/vscode-textmate": "^9.3.1" } }, "node_modules/@shikijs/types": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.23.1.tgz", - "integrity": "sha512-98A5hGyEhzzAgQh2dAeHKrWW4HfCMeoFER2z16p5eJ+vmPeF6lZ/elEne6/UCU551F/WqkopqRsr1l2Yu6+A0g==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.3.tgz", + "integrity": "sha512-FPMrJ69MNxhRtldRk69CghvaGlbbN3pKRuvko0zvbfa2dXp4pAngByToqS5OY5jvN8D7LKR4RJE8UvzlCOuViw==", "dev": true, "dependencies": { - "@shikijs/vscode-textmate": "^9.3.0", + "@shikijs/vscode-textmate": "^9.3.1", "@types/hast": "^3.0.4" } }, "node_modules/@shikijs/vscode-textmate": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", - "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.1.tgz", + "integrity": "sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==", "dev": true }, "node_modules/@sinclair/typebox": { @@ -3156,6 +3092,15 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -3244,17 +3189,21 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" } }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "optional": true + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -3285,9 +3234,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", - "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz", + "integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==", "dev": true, "dependencies": { "@types/node": "*", @@ -3297,9 +3246,9 @@ } }, "node_modules/@types/express-session": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.0.tgz", - "integrity": "sha512-27JdDRgor6PoYlURY+Y5kCakqp5ulC0kmf7y+QwaY+hv9jEFuQOThgkjyA53RP3jmKuBsH5GR6qEfFmvb8mwOA==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-S6TkD/lljxDlQ2u/4A70luD8/ZxZcrU5pQwI1rVXCiaVIywoFgbA+PIUNDjPhQpPdK0dGleLtYc/y7XWBfclBg==", "dev": true, "dependencies": { "@types/express": "*" @@ -3335,8 +3284,7 @@ "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -3387,11 +3335,16 @@ "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", - "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "optional": true + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -3404,8 +3357,7 @@ "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/morgan": { "version": "1.9.9", @@ -3426,17 +3378,17 @@ } }, "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/nodemailer": { - "version": "6.4.16", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.16.tgz", - "integrity": "sha512-uz6hN6Pp0upXMcilM61CoKyjT7sskBoOWpptkjjJp8jIMlTdc3xG01U7proKkXzruMS4hS0zqtHNkNPFB20rKQ==", + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", "dev": true, "dependencies": { "@types/node": "*" @@ -3496,20 +3448,44 @@ "node_modules/@types/qs": { "version": "6.9.17", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", - "dev": true + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "optional": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -3519,7 +3495,6 @@ "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -3560,6 +3535,12 @@ "@types/serve-static": "*" } }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "optional": true + }, "node_modules/@types/ua-parser-js": { "version": "0.7.39", "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", @@ -3613,16 +3594,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", - "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz", + "integrity": "sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/type-utils": "8.13.0", - "@typescript-eslint/utils": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/type-utils": "8.18.1", + "@typescript-eslint/utils": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3637,24 +3618,20 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", - "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.1.tgz", + "integrity": "sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/typescript-estree": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", "debug": "^4.3.4" }, "engines": { @@ -3665,22 +3642,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz", + "integrity": "sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3691,13 +3664,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", - "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz", + "integrity": "sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/typescript-estree": "8.18.1", + "@typescript-eslint/utils": "8.18.1", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -3708,16 +3681,15 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.1.tgz", + "integrity": "sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3728,13 +3700,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz", + "integrity": "sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3749,10 +3721,8 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { @@ -3768,15 +3738,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.1.tgz", + "integrity": "sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/typescript-estree": "8.18.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3786,17 +3756,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz", + "integrity": "sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.18.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3806,10 +3777,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true }, "node_modules/abbrev": { @@ -3817,6 +3800,18 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3915,7 +3910,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4089,15 +4084,15 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4107,15 +4102,15 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4141,19 +4136,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -4162,6 +4156,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -4195,6 +4198,15 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4238,9 +4250,9 @@ } }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -4309,13 +4321,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", + "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { @@ -4336,12 +4348,12 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4394,6 +4406,25 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -4447,6 +4478,14 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4517,9 +4556,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "dev": true, "funding": [ { @@ -4536,9 +4575,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -4570,10 +4609,9 @@ } }, "node_modules/bson": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", - "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==", - "license": "Apache-2.0", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", "engines": { "node": ">=16.20.1" } @@ -4608,15 +4646,42 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4649,9 +4714,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001679", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", - "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "dev": true, "funding": [ { @@ -4812,7 +4877,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, + "devOptional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -4850,7 +4915,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -4862,7 +4927,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "devOptional": true }, "node_modules/color-support": { "version": "1.1.3", @@ -5092,9 +5157,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -5171,12 +5236,12 @@ } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -5204,9 +5269,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { "ms": "^2.1.3" }, @@ -5252,6 +5317,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -5401,9 +5467,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "engines": { "node": ">=12" }, @@ -5411,6 +5477,45 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", @@ -5458,9 +5563,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.55", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", - "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==", + "version": "1.5.75", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz", + "integrity": "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==", "dev": true }, "node_modules/emittery": { @@ -5495,6 +5600,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/engine.io": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", @@ -5531,6 +5645,22 @@ "node": ">= 0.6" } }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -5566,57 +5696,59 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.6.tgz", + "integrity": "sha512-Ifco6n3yj2tMZDWNLyloZrytt9lqqlwvS83P3HtaETR0NUOYnIULGGHpktqYGObGy+8wc1okO25p8TjemhImvA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "data-view-buffer": "^1.0.1", "data-view-byte-length": "^1.0.1", "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.7", + "get-intrinsic": "^1.2.6", "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", + "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", + "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", + "is-string": "^1.1.1", "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.0.0", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", + "typed-array-byte-offset": "^1.0.3", + "typed-array-length": "^1.0.7", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -5626,12 +5758,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -5674,7 +5803,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -5706,14 +5834,14 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -5765,7 +5893,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -5916,19 +6044,19 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", - "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz", + "integrity": "sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==", "dev": true, "dependencies": { "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.5", + "debug": "^4.3.7", "enhanced-resolve": "^5.15.0", - "eslint-module-utils": "^2.8.1", "fast-glob": "^3.3.2", "get-tsconfig": "^4.7.5", "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3" + "is-glob": "^4.0.3", + "stable-hash": "^0.0.4" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -6105,9 +6233,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "28.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.9.0.tgz", - "integrity": "sha512-rLu1s1Wf96TgUUxSw6loVIkNtUjq1Re7A9QdCCHSohnvXEBAjuL420h0T/fMmkQlNsQP2GhQzEUpYHPfxBkvYQ==", + "version": "28.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.10.0.tgz", + "integrity": "sha512-hyMWUxkBH99HpXT3p8hc7REbEZK3D+nk8vHXGgpB+XXsi0gO4PxMSP+pjfUzb67GnV9yawV9a53eUmcde1CCZA==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -6591,6 +6719,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6640,9 +6777,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -6663,7 +6800,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -6678,6 +6815,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-mongo-sanitize": { @@ -6689,9 +6830,9 @@ } }, "node_modules/express-rate-limit": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.1.tgz", - "integrity": "sha512-KS3efpnpIDVIXopMc65EMbWbUht7qvTCdtCR2dD/IZmi9MIkopYESwyRqLgv8Pfu589+KqDqOdzJWW7AHoACeg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", + "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", "engines": { "node": ">= 16" }, @@ -6699,7 +6840,7 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "4 || 5 || ^5.0.0-beta.1" + "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "node_modules/express-session": { @@ -6772,6 +6913,14 @@ "node >=0.6.0" ] }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6827,6 +6976,28 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -6836,6 +7007,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -6955,6 +7137,42 @@ "node": ">=8" } }, + "node_modules/firebase-admin": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.0.2.tgz", + "integrity": "sha512-YWVpoN+tZVSRXF0qC0gojoF5bSqvBRbnBk8+xUtFiguM2L4vB7f0moAwV1VVWDDHvTnvQ68OyTMpdp6wKo/clw==", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -6970,9 +7188,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true }, "node_modules/fn-args": { @@ -7119,20 +7337,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7142,15 +7346,16 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.7.tgz", + "integrity": "sha512-2g4x+HqTJKM9zcJqBSpjoRmdcPFtJM60J3xJisTQSXBWka5XqyBN/2tNUgma1mztTXyDuUsEtYe5qcs7xYzYQA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -7159,6 +7364,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "optional": true + }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -7188,6 +7399,167 @@ "node": ">=10" } }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gaxios/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/gaxios/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata/node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "optional": true, + "peer": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "peer": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gcp-metadata/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true, + "peer": true + }, + "node_modules/gcp-metadata/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true, + "peer": true + }, + "node_modules/gcp-metadata/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "peer": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/generic-pool": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", @@ -7209,21 +7581,26 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, + "devOptional": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -7254,14 +7631,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -7343,9 +7720,9 @@ } }, "node_modules/globals": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", - "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, "engines": { "node": ">=18" @@ -7390,12 +7767,137 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-auth-library": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz", + "integrity": "sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-auth-library/node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-auth-library/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-auth-library/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/google-gax/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true + }, + "node_modules/google-gax/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7413,6 +7915,37 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gtoken/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/gtoken/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -7435,10 +7968,13 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7456,6 +7992,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -7464,9 +8001,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -7475,9 +8016,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -7517,9 +8058,9 @@ } }, "node_modules/hast-util-to-html": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", - "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz", + "integrity": "sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==", "dev": true, "dependencies": { "@types/hast": "^3.0.0", @@ -7580,6 +8121,22 @@ "node": ">=0.10.0" } }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "optional": true + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7611,6 +8168,25 @@ "node": ">= 0.8" } }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -7741,14 +8317,14 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7763,13 +8339,14 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -7800,12 +8377,15 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7824,13 +8404,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7840,9 +8420,9 @@ } }, "node_modules/is-bun-module": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", - "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", + "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==", "dev": true, "dependencies": { "semver": "^7.6.3" @@ -7873,9 +8453,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -7888,11 +8468,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -7903,12 +8485,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7927,12 +8510,15 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8016,12 +8602,13 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8040,13 +8627,15 @@ } }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -8068,12 +8657,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -8086,7 +8675,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "engines": { "node": ">=8" }, @@ -8095,12 +8683,13 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8110,12 +8699,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8125,12 +8716,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -8157,25 +8748,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -8294,16 +8888,17 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", - "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", + "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", "dev": true, "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "reflect.getprototypeof": "^1.0.8", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -8916,6 +9511,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8941,9 +9544,9 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "bin": { "jsesc": "bin/jsesc" @@ -8952,6 +9555,14 @@ "node": ">=6" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -9080,6 +9691,44 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/jwks-rsa/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", @@ -9155,6 +9804,11 @@ "node": ">= 0.8.0" } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -9187,6 +9841,17 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "optional": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -9255,6 +9920,12 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "optional": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -9276,6 +9947,31 @@ "yallist": "^3.0.2" } }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -9334,6 +10030,14 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdast-util-to-hast": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", @@ -9646,13 +10350,12 @@ "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==" }, "node_modules/mongodb": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.11.0.tgz", - "integrity": "sha512-yVbPw0qT268YKhG241vAMLaDQAPbRyTgo++odSgGc9kXnzOujQI60Iyj23B9sQQFPSvmNPvMZ3dsFz0aN55KgA==", - "license": "Apache-2.0", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.0", + "bson": "^6.10.1", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -9660,7 +10363,7 @@ }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", @@ -9692,79 +10395,33 @@ } }, "node_modules/mongodb-connection-string-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", - "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" - } - }, - "node_modules/mongoose": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.1.tgz", - "integrity": "sha512-l7DgeY1szT98+EKU8GYnga5WnyatAu+kOQ2VlVX1Mxif6A0Umt0YkSiksCiyGxzx8SPhGe9a53ND1GD4yVDrPA==", - "dependencies": { - "bson": "^6.7.0", - "kareem": "2.6.3", - "mongodb": "~6.10.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "17.1.3" - }, - "engines": { - "node": ">=16.20.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose/node_modules/mongodb": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", - "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.2.tgz", + "integrity": "sha512-mLWynmZS1v8HTeMxyLhskQncS1SkrjW1eLNuFDYGQMQ/5QrFrxTLNwWXeCRZeKT2lXyaxW8bnJC9AKPT9jYMkw==", + "dependencies": { + "bson": "^6.10.1", + "kareem": "2.6.3", + "mongodb": "~6.12.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" } }, "node_modules/morgan": { @@ -9914,6 +10571,14 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -9921,9 +10586,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true }, "node_modules/nodemailer": { @@ -9935,9 +10600,9 @@ } }, "node_modules/nodemon": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", - "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -10041,9 +10706,9 @@ } }, "node_modules/npm": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.1.tgz", - "integrity": "sha512-yJUw03xLqjiv1D52oHeoS5qmOEC5hkJlhP1cWlSrCgshuxWVyFEEK3M3hLC0NwbTaklLTYrhoIanYsuNP5WUKg==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.2.tgz", + "integrity": "sha512-iriPEPIkoMYUy3F6f3wwSZAU93E0Eg6cHwIR6jzzOXWSy+SD/rOODEs74cVONHKSx2obXtuUoyidVEhISrisgQ==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -10119,8 +10784,8 @@ "@npmcli/arborist": "^8.0.0", "@npmcli/config": "^9.0.0", "@npmcli/fs": "^4.0.0", - "@npmcli/map-workspaces": "^4.0.1", - "@npmcli/package-json": "^6.0.1", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.0", "@npmcli/promise-spawn": "^8.0.2", "@npmcli/redact": "^3.0.0", "@npmcli/run-script": "^9.0.1", @@ -10137,7 +10802,7 @@ "graceful-fs": "^4.2.11", "hosted-git-info": "^8.0.2", "ini": "^5.0.0", - "init-package-json": "^7.0.1", + "init-package-json": "^7.0.2", "is-cidr": "^5.1.0", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^9.0.0", @@ -10156,7 +10821,7 @@ "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^10.2.0", + "node-gyp": "^11.0.0", "nopt": "^8.0.0", "normalize-package-data": "^7.0.0", "npm-audit-report": "^6.0.0", @@ -10409,7 +11074,7 @@ } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "4.0.1", + "version": "4.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -10484,7 +11149,7 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "6.0.1", + "version": "6.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -10531,14 +11196,14 @@ } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "9.0.1", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^4.0.0", "@npmcli/package-json": "^6.0.0", "@npmcli/promise-spawn": "^8.0.0", - "node-gyp": "^10.0.0", + "node-gyp": "^11.0.0", "proc-log": "^5.0.0", "which": "^5.0.0" }, @@ -11111,7 +11776,7 @@ } }, "node_modules/npm/node_modules/init-package-json": { - "version": "7.0.1", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -11169,11 +11834,6 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/isexe": { "version": "2.0.0", "inBundle": true, @@ -11595,16 +12255,8 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/negotiator": { - "version": "0.6.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/npm/node_modules/node-gyp": { - "version": "10.2.0", + "version": "11.0.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -11612,189 +12264,76 @@ "exponential-backoff": "^3.1.1", "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^4.1.0", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.5", - "tar": "^6.2.1", - "which": "^4.0.0" + "tar": "^7.4.3", + "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { - "version": "2.2.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/fs": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { - "version": "18.0.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/isexe": { - "version": "3.1.1", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=16" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { - "version": "13.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/minipass-fetch": { - "version": "3.0.5", + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minipass": "^7.0.4", + "rimraf": "^5.0.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">= 18" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "7.2.1", + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, + "license": "MIT", "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { - "version": "4.2.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/ssri": { - "version": "10.0.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/unique-filename": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^4.0.0" + "node": ">=10" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/unique-slug": { - "version": "4.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", "inBundle": true, "license": "ISC", "dependencies": { - "imurmurhash": "^0.1.4" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/which": { - "version": "4.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=18" } }, "node_modules/npm/node_modules/nopt": { @@ -12748,6 +13287,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -12769,14 +13317,16 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -12833,12 +13383,13 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -12892,14 +13443,14 @@ } }, "node_modules/oniguruma-to-es": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.4.1.tgz", - "integrity": "sha512-rNcEohFz095QKGRovP/yqPIKc+nP+Sjs4YTHMv33nMePGKrq/r2eu9Yh4646M5XluGJsUnmwoXuiXE69KDs+fQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.8.0.tgz", + "integrity": "sha512-rY+/a6b+uCgoYIL9itjY0x99UUDHXmGaw7Jjk5ZvM/3cxDJifyxFr/Zm4tTmF6Tre18gAakJo7AzhKUeMNLgHA==", "dev": true, "dependencies": { "emoji-regex-xs": "^1.0.0", - "regex": "^5.0.0", - "regex-recursion": "^4.2.1" + "regex": "^5.0.2", + "regex-recursion": "^5.0.0" } }, "node_modules/openapi-types": { @@ -12941,7 +13492,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "devOptional": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -13125,9 +13676,9 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/path-type": { "version": "4.0.0", @@ -13218,9 +13769,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -13315,6 +13866,42 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "optional": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -13333,11 +13920,14 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/psl": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.10.0.tgz", - "integrity": "sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", "dependencies": { "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" } }, "node_modules/pstree.remy": { @@ -13493,9 +14083,6 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", - "workspaces": [ - "./packages/*" - ], "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.0", @@ -13506,18 +14093,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", + "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "dunder-proto": "^1.0.1", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -13569,9 +14157,9 @@ } }, "node_modules/regex-recursion": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-4.2.1.tgz", - "integrity": "sha512-QHNZyZAeKdndD1G3bKAbBEKOSSK4KOHQrAJ01N1LJeb0SoH4DJIeFhp0uUpETgONifS4+P3sOgoA1dhzgrQvhA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.0.0.tgz", + "integrity": "sha512-UwyOqeobrCCqTXPcsSqH4gDhOjD5cI/b8kjngWgSZbxYh5yVjAwTjO5+hAuPRNiuR70+5RlWSs+U9PVcVcW9Lw==", "dev": true, "dependencies": { "regex-utilities": "^2.3.0" @@ -13614,15 +14202,15 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -13637,9 +14225,9 @@ "dev": true }, "node_modules/regjsparser": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", - "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "dependencies": { "jsesc": "~3.0.2" @@ -13648,6 +14236,18 @@ "regjsparser": "bin/parser" } }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -13704,24 +14304,27 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13757,14 +14360,37 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "engines": { "node": ">=10" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "optional": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -13823,14 +14449,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -13866,14 +14493,14 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -13962,6 +14589,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -14016,37 +14644,91 @@ } }, "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/shiki": { - "version": "1.23.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.23.1.tgz", - "integrity": "sha512-8kxV9TH4pXgdKGxNOkrSMydn1Xf6It8lsle0fiqxf7a1149K1WGtdOu3Zb91T5r1JpvRPxqxU3C2XdZZXQnrig==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.24.3.tgz", + "integrity": "sha512-eMeX/ehE2IDKVs71kB4zVcDHjutNcOtm+yIRuR4sA6ThBbdFI0DffGJiyoKCodj0xRGxIoWC3pk/Anmm5mzHmA==", "dev": true, "dependencies": { - "@shikijs/core": "1.23.1", - "@shikijs/engine-javascript": "1.23.1", - "@shikijs/engine-oniguruma": "1.23.1", - "@shikijs/types": "1.23.1", - "@shikijs/vscode-textmate": "^9.3.0", + "@shikijs/core": "1.24.3", + "@shikijs/engine-javascript": "1.24.3", + "@shikijs/engine-oniguruma": "1.24.3", + "@shikijs/types": "1.24.3", + "@shikijs/vscode-textmate": "^9.3.1", "@types/hast": "^3.0.4" } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -14108,7 +14790,6 @@ "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", @@ -14131,6 +14812,22 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -14143,6 +14840,38 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -14209,6 +14938,12 @@ "node": ">=0.10.0" } }, + "node_modules/stable-hash": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", + "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", + "dev": true + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -14238,6 +14973,21 @@ "node": ">= 0.8" } }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "optional": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "optional": true + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -14341,15 +15091,18 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14359,15 +15112,19 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14444,6 +15201,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "optional": true + }, "node_modules/superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -14667,6 +15436,77 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/teeny-request/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true + }, + "node_modules/teeny-request/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/telware-backend": { "resolved": "", "link": true @@ -14791,9 +15631,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "engines": { "node": ">=16" @@ -15037,8 +15877,7 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tsx": { "version": "4.19.2", @@ -15121,30 +15960,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -15154,17 +15993,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -15174,17 +16014,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -15246,14 +16086,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.13.0.tgz", - "integrity": "sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.1.tgz", + "integrity": "sha512-Mlaw6yxuaDEPQvb/2Qwu3/TfgeBHy9iTJ3mTwe7OvpPmF6KPQjVOfGyEJpPv6Ez2C34OODChhXrzYw/9phI0MQ==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.13.0", - "@typescript-eslint/parser": "8.13.0", - "@typescript-eslint/utils": "8.13.0" + "@typescript-eslint/eslint-plugin": "8.18.1", + "@typescript-eslint/parser": "8.18.1", + "@typescript-eslint/utils": "8.18.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -15262,10 +16102,9 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/ua-parser-js": { @@ -15316,15 +16155,18 @@ "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15337,9 +16179,9 @@ "dev": true }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", @@ -15633,6 +16475,27 @@ "node": ">=12" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-url": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", @@ -15661,39 +16524,43 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -15727,15 +16594,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -15766,7 +16634,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "devOptional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15829,7 +16697,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" } @@ -15865,7 +16733,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, + "devOptional": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -15883,7 +16751,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=12" } @@ -15901,7 +16769,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index ddc1441..3e4faf8 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,9 @@ }, "dependencies": { "@faker-js/faker": "^9.0.3", - "axios": "^1.7.7", + "@google/generative-ai": "^0.21.0", + "@huggingface/inference": "^2.8.1", + "axios": "^1.7.9", "bcrypt": "^5.1.1", "body-parser": "^1.20.3", "connect-redis": "^7.1.1", @@ -90,6 +92,7 @@ "express-mongo-sanitize": "^2.2.0", "express-rate-limit": "^7.4.1", "express-session": "^1.18.1", + "firebase-admin": "^13.0.2", "helmet": "^8.0.0", "hpp": "^0.2.3", "jsonwebtoken": "^9.0.2", diff --git a/src/app.ts b/src/app.ts index f486714..e635a58 100644 --- a/src/app.ts +++ b/src/app.ts @@ -36,11 +36,11 @@ const swaggerOptions = { }, servers: [ { - url: 'http://localhost:3000/api/v1/', - description: 'Local server', + url: `${process.env.SERVER_URL}/api/v1`, + description: 'HTTP server', }, { - url: 'ws://localhost:3000', + url: process.env.WEBSOCKET_URL, description: 'WebSocket server', }, ], diff --git a/src/config/allowedOrigins.json b/src/config/allowedOrigins.json index d712796..4247c98 100644 --- a/src/config/allowedOrigins.json +++ b/src/config/allowedOrigins.json @@ -3,6 +3,7 @@ "http://localhost:5174", "http://127.0.0.1:5174", "http://telware.tech", + "http://dev.telware.tech", "http://testing.telware.tech", "http://api.telware.tech", "http://api.testing.telware.tech", diff --git a/src/config/firebase.ts b/src/config/firebase.ts new file mode 100644 index 0000000..6350540 --- /dev/null +++ b/src/config/firebase.ts @@ -0,0 +1,13 @@ +import admin from 'firebase-admin'; + +const serviceAccount = JSON.parse( + process.env.FIREBASE_SERVICE_ACCOUNT as string +); + +admin.initializeApp({ + credential: admin.credential.cert(serviceAccount), +}); + +export const messaging = admin.messaging(); + +export default admin; diff --git a/src/controllers/authController.ts b/src/controllers/authController.ts index 0043ebe..ba3f2b3 100644 --- a/src/controllers/authController.ts +++ b/src/controllers/authController.ts @@ -71,18 +71,24 @@ export const signup = catchAsync( export const login = catchAsync( async (req: Request, res: Response, next: NextFunction) => { const { email, password } = req.body; + const user = await User.findOne({ email }); if (!user) return next( new AppError('No user is found with this email address', 404) ); + if (user.accountStatus !== 'active') { + return next(new AppError('Your account is deactivated or banned.', 403)); + } + const message: string = await validateBeforeLogin(email, password); if (message === 'please verify your email first to be able to login') return next(new AppError(message, 403)); if (message !== 'validated') return next(new AppError(message, 400)); await saveSession(user._id as ObjectId, req); + res.status(200).json({ status: 'success', message: 'logged in successfully', diff --git a/src/controllers/chatController.ts b/src/controllers/chatController.ts index ab0630f..61fd401 100644 --- a/src/controllers/chatController.ts +++ b/src/controllers/chatController.ts @@ -1,13 +1,12 @@ import AppError from '@base/errors/AppError'; import Chat from '@base/models/chatModel'; import Message from '@base/models/messageModel'; -import NormalChat from '@base/models/normalChatModel'; import User from '@base/models/userModel'; import { getChats, getLastMessage, - unmute, deleteChatPictureFile, + getUnreadMessages, } from '@base/services/chatService'; import IUser from '@base/types/user'; import catchAsync from '@base/utils/catchAsync'; @@ -15,7 +14,8 @@ import { NextFunction, Request, Response } from 'express'; import mongoose from 'mongoose'; import GroupChannel from '@base/models/groupChannelModel'; import crypto from 'crypto'; -import Invite from '@base/models/invite'; +import Invite from '@base/models/inviteModel'; +import VoiceCall from '@base/models/voiceCallModel'; export const getAllChats = catchAsync( async (req: Request, res: Response, next: NextFunction) => { @@ -38,16 +38,20 @@ export const getAllChats = catchAsync( ) ), ]; - const members = await User.find( - { _id: { $in: memberIds } }, - 'username screenFirstName screenLastName phoneNumber photo status isAdmin stories blockedUsers' - ); - const lastMessages = await getLastMessage(allChats); + + const [members, lastMessages, unreadMessages] = await Promise.all([ + User.find( + { _id: { $in: memberIds } }, + 'username screenFirstName screenLastName phoneNumber photo status isAdmin stories blockedUsers' + ), + getLastMessage(allChats), + getUnreadMessages(allChats, user), + ]); res.status(200).json({ status: 'success', message: 'Chats retrieved successfuly', - data: { chats: allChats, members, lastMessages }, + data: { chats: allChats, members, lastMessages, unreadMessages }, }); } ); @@ -56,15 +60,32 @@ export const getMessages = catchAsync( async (req: Request, res: Response, next: NextFunction) => { const { chatId } = req.params; - const page: number = parseInt(req.query.page as string, 10) || 1; + const pageByMsgId = req.query.page === '0' ? undefined : req.query.page; const limit: number = parseInt(req.query.limit as string, 10) || 20; - const skip: number = (page - 1) * limit; - const messages = await Message.find({ chatId }).limit(limit).skip(skip); + const filter: any = { chatId }; + if (req.query.timestamp) { + filter.timestamp = { $gte: req.query.timestamp }; + } + else if (pageByMsgId) { + const message = await Message.findById(pageByMsgId); + filter.timestamp = { $lt: message.timestamp }; + } + + const messages = await Message.find(filter) + .sort({ timestamp: -1 }) + .limit(limit); + + if (!messages || messages.length === 0) { + return next(new AppError('No messages found', 404)); + } + + messages.reverse(); + const nextPage = messages.length < limit ? undefined : messages[0]._id; res.status(200).json({ status: 'success', message: 'messages retreived successfuly', - data: { messages, nextPage: page + 1 }, + data: { messages, nextPage }, }); } ); @@ -83,41 +104,6 @@ export const postMediaFile = catchAsync(async (req: any, res: Response) => { }); }); -export const enableSelfDestructing = catchAsync( - async (req: Request, res: Response, next: NextFunction) => { - const { chatId } = req.params; - const { destructionDuration } = req.body; - const destructionTimestamp = Date.now(); - if (!destructionDuration) - return next(new AppError('missing required fields', 400)); - const chat = await NormalChat.findByIdAndUpdate(chatId, { - destructionDuration, - destructionTimestamp, - }); - - if (!chat) return next(new AppError('No chat with the provided id', 404)); - res.status(200).json({ - status: 'success', - message: 'Destruction time is enabled successfuly', - }); - } -); - -export const disableSelfDestructing = catchAsync( - async (req: Request, res: Response, next: NextFunction) => { - const { chatId } = req.params; - const chat = NormalChat.findByIdAndUpdate(chatId, { - destructionTimestamp: undefined, - destructionDuration: undefined, - }); - if (!chat) return next(new AppError('No chat with the provided id', 404)); - res.status(200).json({ - status: 'success', - message: 'Destruction time is disabled successfuly', - }); - } -); - export const getChat = catchAsync(async (req: Request, res: Response) => { const { chatId } = req.params; const chat = await Chat.findById(chatId).populate( @@ -172,49 +158,6 @@ export const getChatMembers = catchAsync( } ); -export const muteChat = catchAsync( - async (req: Request, res: Response, next: NextFunction) => { - const { chatId } = req.params; - const { muteDuration } = req.body; - const user: IUser = req.user as IUser; - if (!user) return next(new AppError('login first', 403)); - if (!muteDuration) - return next(new AppError('missing required fields', 400)); - user.chats.forEach((c: any) => { - if (c.chat.equals(chatId)) { - c.isMuted = true; - c.muteDuration = muteDuration; - } - }); - await user.save({ validateBeforeSave: false }); - unmute(user, chatId, muteDuration); - res.status(200).json({ - status: 'success', - message: 'Chat muted successfully', - }); - } -); - -export const unmuteChat = catchAsync( - async (req: Request, res: Response, next: NextFunction) => { - const { chatId } = req.params; - const user: IUser = req.user as IUser; - if (!user) return next(new AppError('login first', 403)); - - user.chats.forEach((c: any) => { - if (c.chat.equals(chatId)) { - c.isMuted = false; - c.muteDuration = undefined; - } - }); - await user.save({ validateBeforeSave: false }); - res.status(200).json({ - status: 'success', - message: 'Chat unmuted successfully', - }); - } -); - export const updateChatPicture = catchAsync(async (req: any, res: Response) => { const { chatId } = req.params; @@ -293,3 +236,61 @@ export const join = catchAsync( }); } ); + +export const getVoiceCallsInChat = catchAsync( + async (req: Request, res: Response, next: NextFunction) => { + const { chatId } = req.params; + + const voiceCalls = await VoiceCall.find({ chatId }); + + res.status(200).json({ + status: 'success', + message: 'voice calls retrieved successfuly', + data: { + voiceCalls, + }, + }); + } +); +export const filterChatGroups = catchAsync( + async (req: Request, res: Response, next: NextFunction) => { + const { chatId } = req.params; + const groupChannel = await GroupChannel.findById(chatId); + if (!groupChannel) { + return res.status(404).json({ + status: 'fail', + message: 'Group/Channel not found with the given chatId', + }); + } + + groupChannel.isFilterd = true; + await groupChannel.save(); + res.status(200).json({ + status: 'success', + message: 'isFiltered set to true successfully', + data: groupChannel, + }); + } +); +export const unfilterChatGroups = catchAsync( + async (req: Request, res: Response, next: NextFunction) => { + const { chatId } = req.params; + + const groupChannel = await GroupChannel.findById(chatId); + + if (!groupChannel) { + return res.status(404).json({ + status: 'fail', + message: 'GroupChannel not found with the given chatId', + }); + } + groupChannel.isFilterd = false; + await groupChannel.save(); + + res.status(200).json({ + status: 'success', + message: 'isFiltered set to true successfully', + data: groupChannel, + }); + } +); diff --git a/src/controllers/searchController.ts b/src/controllers/searchController.ts index c33d5ea..0a14b8b 100644 --- a/src/controllers/searchController.ts +++ b/src/controllers/searchController.ts @@ -1,68 +1,170 @@ -import { Response ,NextFunction } from 'express'; +import { Response, NextFunction } from 'express'; import Message from '@base/models/messageModel'; import Chat from '@base/models/chatModel'; import catchAsync from '@utils/catchAsync'; +import GroupChannel from '@base/models/groupChannelModel'; +import User from '@base/models/userModel'; +import IUser from '@base/types/user'; +import IGroupChannel from '@base/types/groupChannel'; +export const searchMessages = catchAsync( + async (req: any, res: Response, next: NextFunction) => { + const globalSearchResult: { + groups: IGroupChannel[]; + users: IUser[]; + channels: IGroupChannel[]; + } = { + groups: [], + users: [], + channels: [], + }; -export const searchMessages = catchAsync(async (req: any, res: Response, next: NextFunction) => { - try { - const { query, searchSpace, filter, isGlobalSearch } = req.query; + const { query, searchSpace, filter, isGlobalSearch } = req.body; + // Input validation if (!query || !searchSpace || typeof isGlobalSearch === 'undefined') { - return res.status(400).json({ message: 'Query, searchSpace, and isGlobalSearch are required' }); + return res + .status(400) + .json({ + message: 'Query, searchSpace, and isGlobalSearch are required', + }); } - const searchConditions: any = { - content: { $regex: query as string, $options: 'i' }, - }; + const searchConditions: any = { content: { $regex: query, $options: 'i' } }; + // Handle contentType filter if (filter) { - const filterTypes = (filter as string).split(','); + const filterTypes = filter.split(','); searchConditions.contentType = { $in: filterTypes }; } - const spaces = (searchSpace as string).split(','); - if (spaces.includes('chats')) searchConditions.chatId = { $exists: true }; - if (spaces.includes('channels')) searchConditions.messageType = 'channel'; - if (spaces.includes('groups')) searchConditions.messageType = 'group'; - + // Prepare chat type filters + const spaces = searchSpace.split(','); + const chatTypeConditions: any[] = []; + + if (spaces.includes('chats')) { + chatTypeConditions.push({ type: 'private' }); + } + if (spaces.includes('channels')) { + chatTypeConditions.push({ type: 'channel' }); + } + if (spaces.includes('groups')) { + chatTypeConditions.push({ type: 'group' }); + } + + // Limit search to user's chats unless global search + let chatFilter: any = {}; const userChats = await Chat.find({ - members: { $elemMatch: { _id: req.user._id } }, - }).select('_id'); - - console.log(userChats) - const chatIds = userChats.map((chat) => chat._id); - searchConditions.chatId = { $in: chatIds }; + members: { $elemMatch: { user: req.user._id } }, + }).select('_id type'); + + // Filter user chats by type + const filteredChats = userChats.filter((chat) => + chatTypeConditions.length > 0 + ? chatTypeConditions.some((cond) => cond.type === chat.type) + : true + ); + + const chatIds = filteredChats.map((chat) => chat._id); + chatFilter = { chatId: { $in: chatIds } }; - console.log(searchConditions); + // Combine filters + const finalSearchConditions = { ...searchConditions, ...chatFilter }; - const messages = await Message.find(searchConditions) + // Fetch messages and populate references + const messages = await Message.find(finalSearchConditions) .populate('senderId', 'username') - .populate('chatId', 'name') - .limit(50) - .exec(); + .populate({ + path: 'chatId', + select: 'name type', + }) + .limit(50); + + const groups: string[] = []; + messages.forEach((message: any) => { + groups.push(message.chatId.name); + console.log(message.chatId.name); + }); + + // Search for group channels by name in the groups array + const _groupChannels = await GroupChannel.find({ + name: { $in: groups }, + }).select('name type picture'); + + // Now, populate the chatId with name, type, and picture + const updatedMessages = await Message.find(finalSearchConditions) + .populate({ + path: 'chatId', + select: 'name type picture', + match: { name: { $in: groups } }, // Ensure the chatId matches the groups array + }) + .limit(50); + + // This will print the 'name' from the chatId + // Global Search for Groups, Channels, and Chats + if (isGlobalSearch) { + // Groups and Channels by name + const groupsAndChannels = await GroupChannel.find({ + name: { $regex: query, $options: 'i' }, + }).select('name type picture'); + + globalSearchResult.groups = groupsAndChannels.filter( + (gc: IGroupChannel) => gc.type === 'group' + ); + globalSearchResult.channels = groupsAndChannels.filter( + (gc: IGroupChannel) => gc.type === 'channel' + ); + + // Users (to find chats involving usernames) + const users = await User.find({ + $or: [ + { screenFirstName: { $regex: query, $options: 'i' } }, + { screenLastName: { $regex: query, $options: 'i' } }, + { username: { $regex: query, $options: 'i' } }, + ], + }).select( + 'name username _id screenFirstName screenLastName phoneNumber photo bio accountStatus stories' + ); + + globalSearchResult.users = users; + + // Chats where the user is a member and the username matches + // const userIds = users.map((user) => user._id); + // const chats = await Chat.find({ + // members: { $elemMatch: { user: { $in: userIds } } }, + // type: 'private', + // }).select('type members'); + + // globalSearchResult.chats.push(...chats); + } res.status(200).json({ success: true, - data: messages, + data: { + searchResult: updatedMessages, + globalSearchResult, + }, }); - } catch (error) { - console.error('Error in searchMessages:', error); - res.status(500).json({ success: false, message: 'Internal Server Error' }); } -}); +); +export const searchMessagesDummmy = catchAsync( + async (req: any, res: Response, next: NextFunction) => { + try { + const { query, searchSpace, isGlobalSearch } = req.query; -export const searchMessagesDummmy = catchAsync(async (req: any, res: Response, next: NextFunction) => { - try { - const userId = req.user.id; - const { query, searchSpace, filter, isGlobalSearch } = req.query; - - if (!query || !searchSpace || typeof isGlobalSearch === 'undefined') { - return res.status(400).json({ message: 'Query, searchSpace, and isGlobalSearch are required' }); + if (!query || !searchSpace || typeof isGlobalSearch === 'undefined') { + return res + .status(400) + .json({ + message: 'Query, searchSpace, and isGlobalSearch are required', + }); + } + } catch (error) { + console.error('Error in searchMessages:', error); + res + .status(500) + .json({ success: false, message: 'Internal Server Error' }); } - } catch (error) { - console.error('Error in searchMessages:', error); - res.status(500).json({ success: false, message: 'Internal Server Error' }); } -}); +); diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts index 2c7040c..1ca3045 100644 --- a/src/controllers/userController.ts +++ b/src/controllers/userController.ts @@ -4,6 +4,7 @@ import catchAsync from '@base/utils/catchAsync'; import { Request, Response } from 'express'; import deletePictureFile from '@base/services/userService'; import IUser from '@base/types/user'; +import GroupChannel from '@base/models/groupChannelModel'; interface GetUser extends Request { params: { @@ -13,22 +14,24 @@ interface GetUser extends Request { //TODO: add a user here that would contain the user data. } -export const getCurrentUser = catchAsync(async (req: GetUser, res: Response) => { - const userId = req.user.id; - - const user = await User.findById(userId); - - if (!user) { - throw new AppError('No User exists with this ID', 404); +export const getCurrentUser = catchAsync( + async (req: GetUser, res: Response) => { + const userId = req.user.id; + + const user = await User.findById(userId); + + if (!user) { + throw new AppError('No User exists with this ID', 404); + } + return res.status(200).json({ + status: 'success', + message: 'User retrieved successfuly', + data: { + user, + }, + }); } - return res.status(200).json({ - status: 'success', - message: 'User retrieved successfuly', - data: { - user, - }, - }); -}); +); export const updateCurrentUser = catchAsync(async (req: any, res: Response) => { const userData = req.body; @@ -83,7 +86,14 @@ export const getUser = catchAsync(async (req: GetUser, res: Response) => { throw new AppError('No User exists with this ID', 404); } - const fieldsToGet = ['username', 'screenFirstName', 'screenLastName', 'email', 'status', 'bio']; + const fieldsToGet = [ + 'username', + 'screenFirstName', + 'screenLastName', + 'email', + 'status', + 'bio', + ]; if ( user.picturePrivacy === 'everyone' || @@ -106,7 +116,7 @@ export const getUser = catchAsync(async (req: GetUser, res: Response) => { export const getAllUsers = catchAsync(async (req: Request, res: Response) => { const users = await User.find( {}, - 'username screenFirstName screenLastName email photo status bio' + 'username screenFirstName screenLastName email photo status bio accountStatus' ); return res.status(200).json({ @@ -122,7 +132,11 @@ export const updateBio = catchAsync(async (req: any, res: Response) => { const { bio } = req.body; const userId = req.user.id; - const user = await User.findByIdAndUpdate(userId, { bio }, { new: true, runValidators: true }); + const user = await User.findByIdAndUpdate( + userId, + { bio }, + { new: true, runValidators: true } + ); if (!user) { throw new AppError('No User exists with this ID', 404); @@ -160,7 +174,11 @@ export const updateEmail = catchAsync(async (req: any, res: Response) => { const { email } = req.body; const userId = req.user.id; - const user = await User.findByIdAndUpdate(userId, { email }, { new: true, runValidators: true }); + const user = await User.findByIdAndUpdate( + userId, + { email }, + { new: true, runValidators: true } + ); if (!user) { throw new AppError('No User exists with this ID', 404); @@ -262,3 +280,116 @@ export const deletePicture = catchAsync(async (req: any, res: Response) => { data: {}, }); }); + +export const getAllGroups = catchAsync(async (req: Request, res: Response) => { + const groupsAndChannels = await GroupChannel.find(); // Use `find()` in Mongoose to retrieve all documents + + return res.status(200).json({ + status: 'success', + message: 'Groups and Channels retrieved successfully', + data: { + groupsAndChannels, + }, + }); +}); + +export const toggleAutomaticDownload = catchAsync( + async (req: any, res: Response) => { + const userId = req.user.id; + const { enabled } = req.body; + + User.findByIdAndUpdate(userId, { automaticDownloadEnable: enabled }); + return res.status(200).json({ + status: 'success', + message: 'Automatic download settings updated successfully', + data: {}, + }); + } +); + +export const activateUser = catchAsync(async (req: Request, res: Response) => { + const { userId } = req.params; + + const user = await User.findById(userId); + if (!user) { + return res.status(404).json({ + status: 'fail', + message: 'User not found', + }); + } + if (user.accountStatus === 'banned') { + return res.status(400).json({ + status: 'fail', + message: 'User is Banned', + }); + } + user.accountStatus = 'active'; + await user.save(); + + return res.status(200).json({ + status: 'success', + message: 'User activated successfully', + }); +}); + +export const deactivateUser = catchAsync( + async (req: Request, res: Response) => { + const { userId } = req.params; + const user = await User.findById(userId); + if (!user) { + return res.status(404).json({ + status: 'fail', + message: 'User not found', + }); + } + + user.accountStatus = 'deactivated'; + await user.save(); + + return res.status(200).json({ + status: 'success', + message: 'User deactivated successfully', + }); + } +); + +export const banUser = catchAsync(async (req: Request, res: Response) => { + const { userId } = req.params; + + const user = await User.findById(userId); + if (!user) { + return res.status(404).json({ + status: 'fail', + message: 'User not found', + }); + } + + user.accountStatus = 'banned'; + await user.save(); + + return res.status(200).json({ + status: 'success', + message: 'User banned successfully', + }); +}); + +export const updateFCMToken = catchAsync(async (req: any, res: Response) => { + const userId = req.user.id; + const { fcmToken } = req.body; + + const user = await User.findOneAndUpdate( + { _id: userId }, + { fcmToken }, + { new: true, runValidators: true } + ); + + if (!user) { + throw new AppError('No User exists with this ID', 404); + } + + return res.status(201).json({ + status: 'success', + message: 'User fcm token updated successfuly', + data: {}, + }); +}); diff --git a/src/database/seed/json/users.json b/src/database/seed/json/users.json index 478e3b7..a01d07a 100644 --- a/src/database/seed/json/users.json +++ b/src/database/seed/json/users.json @@ -1,4 +1,15 @@ [ + { + "email": "admin@gmail.com", + "username": "Hamdy", + "screenFirstName": "Ahmed", + "screenLastName": "Hamdy", + "phoneNumber": "+201063360716", + "password": "12345678", + "passwordConfirm": "12345678", + "accountStatus": "active", + "isAdmin": true + }, { "email": "front1@gmail.com", "username": "Batman", @@ -98,5 +109,25 @@ "password": "12345678", "passwordConfirm": "12345678", "accountStatus": "active" + }, + { + "email": "banned@gmail.com", + "username": "Forever_Banned", + "screenFirstName": "Lonely", + "screenLastName": "Soul", + "phoneNumber": "+201055900090", + "password": "12345678", + "passwordConfirm": "12345678", + "accountStatus": "banned" + }, + { + "email": "deactivated@gmail.com", + "username": "I_HOPE_I_RETURN", + "screenFirstName": "Stolen", + "screenLastName": "Soul", + "phoneNumber": "+201055900090", + "password": "12345678", + "passwordConfirm": "12345678", + "accountStatus": "deactivated" } ] diff --git a/src/database/seed/userSeed.ts b/src/database/seed/userSeed.ts index b05f93d..17a0e16 100644 --- a/src/database/seed/userSeed.ts +++ b/src/database/seed/userSeed.ts @@ -2,8 +2,9 @@ import fs from 'fs'; import { faker } from '@faker-js/faker'; import User from '@models/userModel'; import Message from '@models/messageModel'; -import GroupChannel from '@base/models/groupChannelModel'; -import NormalChat from '@base/models/normalChatModel'; +import GroupChannel from '@models/groupChannelModel'; +import NormalChat from '@models/normalChatModel'; +import { decryptKey, encryptMessage } from '@utils/encryption'; const existingUsers = JSON.parse( fs.readFileSync(`${__dirname}/json/users.json`, 'utf-8') @@ -66,6 +67,14 @@ const createRandomMessage = async (chat: any) => { timestamp: faker.date.recent({ days: 30 }), }; + if (chat.encryptionKey) { + message.content = encryptMessage( + message.content, + decryptKey(chat.encryptionKey, chat.keyAuthTag), + decryptKey(chat.initializationVector, chat.vectorAuthTag) + ); + } + return Message.create(message); }; diff --git a/src/errors/errorHandlers.ts b/src/errors/errorHandlers.ts index 68060e8..ce37ee7 100644 --- a/src/errors/errorHandlers.ts +++ b/src/errors/errorHandlers.ts @@ -32,3 +32,7 @@ export const handleInvalidPrivacyOption = (err: AppError) => { err.message = 'Invalid Privacy Option.'; return new AppError(err.message, 400); }; +export const handleInvalidAuth = (err: AppError) => { + err.message = 'You are not an Admin.'; + return new AppError(err.message, 400); +}; diff --git a/src/errors/globalErrorHandler.ts b/src/errors/globalErrorHandler.ts index e406cc4..d0d3134 100644 --- a/src/errors/globalErrorHandler.ts +++ b/src/errors/globalErrorHandler.ts @@ -5,6 +5,7 @@ import { sendDevError, sendProdError, handleInvalidPrivacyOption, + handleInvalidAuth } from './errorHandlers'; const globalErrorHandler = ( @@ -25,11 +26,22 @@ const globalErrorHandler = ( 'Validation failed: invitePermessionsPrivacy: `nobody` is not a valid enum value for path `invitePermessionsPrivacy`.' ) err = handleInvalidPrivacyOption(err); + if ( + err.message === + "You are not authorized to access this resource" + ) + err = handleInvalidAuth(err); if (err.name === 'ValidationError') err = handleDuplicateKeysError(err); sendProdError(err, res); } + if ( + err.message === + "You are not authorized to access this resource" + ) + err = handleInvalidAuth(err); + }; export default globalErrorHandler; diff --git a/src/middlewares/authMiddleware.ts b/src/middlewares/authMiddleware.ts index 2ed078c..5da0901 100644 --- a/src/middlewares/authMiddleware.ts +++ b/src/middlewares/authMiddleware.ts @@ -4,6 +4,7 @@ import AppError from '@errors/AppError'; import User from '@models/userModel'; import { reloadSession } from '@services/sessionService'; import redisClient from '@base/config/redis'; +import IUser from '@base/types/user'; export const protect = catchAsync( async (req: Request, res: Response, next: NextFunction) => { @@ -45,6 +46,19 @@ export const savePlatformInfo = catchAsync( export const isAdmin = catchAsync( async (req: Request, res: Response, next: NextFunction) => { + const currentUser = req.user as IUser; + if (!currentUser || !currentUser.isAdmin) { + return next(new AppError('You are not authorized to access this resource', 403)); + } + next(); + } +); +export const isActive = catchAsync( + async (req: Request, res: Response, next: NextFunction) => { + const currentUser = req.user as IUser; + if (!currentUser || currentUser.accountStatus !== 'active') { + return next(new AppError('You are not active', 403)); + } next(); } ); diff --git a/src/models/chatModel.ts b/src/models/chatModel.ts index a36f57a..33684e1 100644 --- a/src/models/chatModel.ts +++ b/src/models/chatModel.ts @@ -1,5 +1,6 @@ import mongoose from 'mongoose'; import IChat from '@base/types/chat'; +import { decryptKey } from '@base/utils/encryption'; const chatSchema = new mongoose.Schema( { @@ -41,6 +42,15 @@ const chatSchema = new mongoose.Schema( delete member._id; }); } + if (ret.encryptionKey) { + ret.encryptionKey = decryptKey(ret.encryptionKey, ret.keyAuthTag); + ret.initializationVector = decryptKey( + ret.initializationVector, + ret.vectorAuthTag + ); + delete ret.keyAuthTag; + delete ret.vectorAuthTag; + } return ret; }, }, @@ -52,5 +62,14 @@ chatSchema.virtual('numberOfMembers').get(function () { return Array.isArray(this.members) ? this.members.length : 0; }); +chatSchema.pre('save', function (next) { + if (!this.isModified('members')) return next(); + const uniqueUsers = new Set(this.members.map((m) => m.user.toString())); + if (uniqueUsers.size !== this.members.length) { + return next(new Error('Members must have unique users.')); + } + next(); +}); + const Chat = mongoose.model('Chat', chatSchema); export default Chat; diff --git a/src/models/communicationModel.ts b/src/models/communicationModel.ts index 4290cf4..289a043 100644 --- a/src/models/communicationModel.ts +++ b/src/models/communicationModel.ts @@ -33,5 +33,7 @@ const communicationSchema = new mongoose.Schema( } ); +communicationSchema.index({ timestamp: -1 }, { unique: true, background: true }); + const Communication = mongoose.model('Communication', communicationSchema); export default Communication; diff --git a/src/models/invite.ts b/src/models/inviteModel.ts similarity index 100% rename from src/models/invite.ts rename to src/models/inviteModel.ts diff --git a/src/models/messageModel.ts b/src/models/messageModel.ts index f628205..930c834 100644 --- a/src/models/messageModel.ts +++ b/src/models/messageModel.ts @@ -5,6 +5,8 @@ import Communication from './communicationModel'; const messageSchema = new mongoose.Schema({ content: String, media: String, + mediaName: String, + mediaSize: Number, contentType: { type: String, enum: ['text', 'image', 'GIF', 'sticker', 'audio', 'video', 'file', 'link'], @@ -14,6 +16,10 @@ const messageSchema = new mongoose.Schema({ type: Boolean, default: false, }, + isAppropriate: { + type: Boolean, + default: true, + }, isForward: { type: Boolean, default: false, @@ -26,6 +32,20 @@ const messageSchema = new mongoose.Schema({ type: Boolean, default: false, }, + deliveredTo: [ + { + type: mongoose.Types.ObjectId, + ref: 'User', + default: [], + }, + ], + readBy: [ + { + type: mongoose.Types.ObjectId, + ref: 'User', + default: [], + }, + ], parentMessageId: mongoose.Types.ObjectId, threadMessages: [ { diff --git a/src/models/normalChatModel.ts b/src/models/normalChatModel.ts index 144f930..c6e762f 100644 --- a/src/models/normalChatModel.ts +++ b/src/models/normalChatModel.ts @@ -1,11 +1,44 @@ import mongoose from 'mongoose'; +import crypto from 'crypto'; +import { encryptKey } from '@utils/encryption'; import INormalChat from '@base/types/normalChat'; import Chat from './chatModel'; const normalChatSchema = new mongoose.Schema({ + encryptionKey: { + type: String, + default: crypto.randomBytes(32).toString('hex'), + }, + initializationVector: { + type: String, + default: crypto.randomBytes(16).toString('hex'), + }, + keyAuthTag: { + type: String, + default: '', + }, + vectorAuthTag: { + type: String, + default: '', + }, destructionTimestamp: Date, destructionDuration: Number, }); +normalChatSchema.pre('save', function (next) { + if (!this.isNew) return next(); + const { encrypted: encryptedKey, authTag: keyAuthTag } = encryptKey( + this.encryptionKey + ); + const { encrypted: encryptedVector, authTag: vectorAuthTag } = encryptKey( + this.initializationVector + ); + this.encryptionKey = encryptedKey; + this.keyAuthTag = keyAuthTag; + this.initializationVector = encryptedVector; + this.vectorAuthTag = vectorAuthTag; + next(); +}); + const NormalChat = Chat.discriminator('NormalChat', normalChatSchema); export default NormalChat; diff --git a/src/models/userModel.ts b/src/models/userModel.ts index 42f27d1..4fa9a4c 100644 --- a/src/models/userModel.ts +++ b/src/models/userModel.ts @@ -30,6 +30,10 @@ const userSchema = new mongoose.Schema( message: 'Username can contain only letters, numbers and underscore', }, }, + fcmToken: { + type: String, + default: '', + }, screenFirstName: { type: String, default: '', @@ -233,25 +237,23 @@ const userSchema = new mongoose.Schema( } ); -//TODO: unreadMessages virtual property +userSchema.index({ email: 1 }, { background: true }); -userSchema.index({ email: 1 }, { unique: true, background: true }); - -userSchema.pre('save', async function (next) { +userSchema.pre('save', async function(next) { if (!this.isModified('password') || !this.password) return next(); this.password = await bcrypt.hash(this.password, 12); this.passwordConfirm = undefined; next(); }); -userSchema.pre('save', function (next) { +userSchema.pre('save', function(next) { if (this.provider === 'local') { this.providerId = this._id as string; } next(); }); -userSchema.methods.isCorrectPassword = async function ( +userSchema.methods.isCorrectPassword = async function( candidatePass: string ): Promise { const result = await bcrypt.compare(candidatePass, this.password); @@ -259,7 +261,7 @@ userSchema.methods.isCorrectPassword = async function ( return result; }; -userSchema.methods.passwordChanged = function (tokenIssuedAt: number): boolean { +userSchema.methods.passwordChanged = function(tokenIssuedAt: number): boolean { if ( this.changedPasswordAt && this.changedPasswordAt.getTime() / 1000 > tokenIssuedAt @@ -268,14 +270,7 @@ userSchema.methods.passwordChanged = function (tokenIssuedAt: number): boolean { return false; }; -//FIX: fix this function -userSchema.methods.selectFields = function (): void { - this.select( - '-__v -provider -providerId -password -isAdmin -stories -blockedUsers -contacts -chats -changedPasswordAt -emailVerificationCode -emailVerificationCodeExpires -resetPasswordToken -resetPasswordExpires' - ); -}; - -userSchema.methods.generateSaveConfirmationCode = function (): string { +userSchema.methods.generateSaveConfirmationCode = function(): string { const confirmationCode: string = generateConfirmationCode(); this.emailVerificationCode = crypto .createHash('sha256') @@ -286,7 +281,7 @@ userSchema.methods.generateSaveConfirmationCode = function (): string { return confirmationCode; }; -userSchema.methods.createResetPasswordToken = function (): string { +userSchema.methods.createResetPasswordToken = function(): string { const resetPasswordToken = crypto.randomBytes(32).toString('hex'); this.resetPasswordToken = crypto diff --git a/src/models/voiceCallModel.ts b/src/models/voiceCallModel.ts index acf6d77..16e894e 100644 --- a/src/models/voiceCallModel.ts +++ b/src/models/voiceCallModel.ts @@ -3,14 +3,19 @@ import mongoose from 'mongoose'; import Communication from './communicationModel'; const voiceCallSchema = new mongoose.Schema({ - duration: { - type: Number, - default: 0, - }, - status: { - type: String, - enum: ['declined', 'answered'], + timestamp: { type: Date, default: Date.now }, + duration: { type: Number, default: -1 }, + callType: { type: String, enum: ['group', 'private'], required: true }, + status: { type: String, enum: ['ongoing', 'finished'], default: 'ongoing' }, + senderId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', + required: true, }, + currentParticipants: [ + { type: mongoose.Types.ObjectId, ref: 'User', default: [] }, + ], + chatId: { type: mongoose.Schema.Types.ObjectId, ref: 'Chat', required: true }, }); const VoiceCall = Communication.discriminator('VoiceCall', voiceCallSchema); diff --git a/src/routes/apiRoute.ts b/src/routes/apiRoute.ts index 27dcd26..293d9e1 100644 --- a/src/routes/apiRoute.ts +++ b/src/routes/apiRoute.ts @@ -3,7 +3,7 @@ import authRouter from '@routes/authRoute'; import userRouter from '@routes/userRoute'; import storyRouter from '@base/routes/storyRoute'; import chatRouter from '@base/routes/chatRoute'; -import searchRouter from '@base/routes/searchRoute' +import searchRouter from '@base/routes/searchRoute'; const apiRouter = Router(); diff --git a/src/routes/authRoute.ts b/src/routes/authRoute.ts index d14ffe8..f840376 100644 --- a/src/routes/authRoute.ts +++ b/src/routes/authRoute.ts @@ -13,7 +13,7 @@ import { getLogedInSessions, getCurrentSession, } from '@controllers/authController'; -import { protect } from '@middlewares/authMiddleware'; +import { protect , isActive } from '@middlewares/authMiddleware'; import oauthRouter from '@base/routes/oauthRoute'; const router = Router(); @@ -27,10 +27,11 @@ router.post('/send-confirmation', sendConfirmationCode); router.post('/verify', verifyEmail); router.post('/password/forget', forgotPassword); router.patch('/password/reset/:token', resetPassword); -router.patch('/password/change', protect, changePassword); router.use(protect); +router.patch('/password/change', protect, changePassword); +router.use(isActive); router.get('/me', getCurrentSession); router.get('/sessions', getLogedInSessions); router.post('/logout', logoutSession); diff --git a/src/routes/chatRoute.ts b/src/routes/chatRoute.ts index a3bbf09..33ef9df 100644 --- a/src/routes/chatRoute.ts +++ b/src/routes/chatRoute.ts @@ -3,18 +3,17 @@ import { getAllChats, getMessages, postMediaFile, - enableSelfDestructing, - disableSelfDestructing, getChat, setPrivacy, getChatMembers, - muteChat, - unmuteChat, updateChatPicture, invite, join, + getVoiceCallsInChat, + filterChatGroups, + unfilterChatGroups, } from '@base/controllers/chatController'; -import { protect } from '@base/middlewares/authMiddleware'; +import { protect, isAdmin } from '@base/middlewares/authMiddleware'; import upload from '@base/config/fileUploads'; import restrictTo from '@base/middlewares/chatMiddlewares'; @@ -23,19 +22,24 @@ const router = Router(); router.use(protect); router.get('/', getAllChats); router.post('/media', upload.single('file'), postMediaFile); -router.patch('/picture/:chatId', restrictTo(), upload.single('file'), updateChatPicture); +router.patch( + '/picture/:chatId', + restrictTo(), + upload.single('file'), + updateChatPicture +); router.patch('/privacy/:chatId', restrictTo('admin'), setPrivacy); -router.patch('/destruct/:chatId', restrictTo(), enableSelfDestructing); -router.patch('/un-destruct/:chatId', restrictTo(), disableSelfDestructing); -router.patch('/mute/:chatId', restrictTo(), muteChat); -router.patch('/unmute/:chatId', restrictTo(), unmuteChat); router.get('/invite/:chatId', restrictTo('admin'), invite); router.post('/join/:token', join); +router.get('/voice-calls/:chatId', restrictTo(), getVoiceCallsInChat); router.get('/messages/:chatId', restrictTo(), getMessages); router.get('/members/:chatId', restrictTo(), getChatMembers); router.get('/:chatId', restrictTo(), getChat); +router.patch('/groups/filter/:chatId', isAdmin, filterChatGroups); +router.patch('/groups/unfilter/:chatId', isAdmin, unfilterChatGroups); + export default router; diff --git a/src/routes/searchRoute.ts b/src/routes/searchRoute.ts index bdbbdd6..b039f47 100644 --- a/src/routes/searchRoute.ts +++ b/src/routes/searchRoute.ts @@ -7,6 +7,6 @@ import restrictTo from '@base/middlewares/chatMiddlewares'; const router = Router(); router.use(protect); -router.get('/search-request',searchMessages); +router.post('/search-request',searchMessages); export default router; diff --git a/src/routes/userRoute.ts b/src/routes/userRoute.ts index efc027b..34753de 100644 --- a/src/routes/userRoute.ts +++ b/src/routes/userRoute.ts @@ -19,6 +19,11 @@ import { updatePicture, updateScreenName, updateUsername, + getAllGroups, + activateUser, + deactivateUser, + banUser, + updateFCMToken, } from '@controllers/userController'; import { deleteStory, @@ -27,11 +32,12 @@ import { getStory, postStory, } from '@controllers/storyController'; -import { protect } from '@middlewares/authMiddleware'; +import { protect, isAdmin, isActive } from '@middlewares/authMiddleware'; const router = Router(); router.use(protect); +router.use(isActive); router.use('/privacy', privacyRouter); router.get('/stories', getCurrentUserStory); router.post('/stories', upload.single('file'), postStory); @@ -42,8 +48,13 @@ router.get('/block', getBlockedUsers); router.post('/block/:id', block); router.delete('/block/:id', unblock); +// Admin routes +router.patch('/activate/:userId', isAdmin, activateUser); +router.patch('/deactivate/:userId', isAdmin, deactivateUser); +router.patch('/ban/:userId', isAdmin, banUser); +router.get('/all-groups', isAdmin, getAllGroups); + // User routes -router.get('/', getAllUsers); router.get('/me', getCurrentUser); router.get('/username/check', getCheckUserName); router.patch('/me', updateCurrentUser); @@ -53,9 +64,11 @@ router.patch('/email', updateEmail); router.patch('/username', updateUsername); router.patch('/screen-name', updateScreenName); router.patch('/picture', upload.single('file'), updatePicture); +router.patch('/fcm-token', updateFCMToken); router.delete('/picture', deletePicture); router.get('/contacts/stories', getAllContactsStories); -router.get('/:userId', getUser); router.get('/:userId/stories', getStory); +router.get('/:userId', getUser); +router.get('/', getAllUsers); export default router; diff --git a/src/server.ts b/src/server.ts index 3c93c46..2828727 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,6 +1,7 @@ import http from 'http'; import '@config/env'; import '@config/passport'; +import '@config/firebase'; import mongoDBConnection from '@config/mongoDB'; import app from '@base/app'; import socketSetup from './sockets/socket'; diff --git a/src/services/authService.ts b/src/services/authService.ts index 5d813b0..1152491 100644 --- a/src/services/authService.ts +++ b/src/services/authService.ts @@ -180,4 +180,4 @@ export const createOAuthUser = async ( }); await newUser.save({ validateBeforeSave: false }); return newUser; -}; \ No newline at end of file +}; diff --git a/src/services/chatService.ts b/src/services/chatService.ts index d668016..a220abe 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,19 +1,21 @@ import mongoose from 'mongoose'; import NormalChat from '@base/models/normalChatModel'; import Message from '@base/models/messageModel'; -import { Socket } from 'socket.io'; +import { Server, Socket } from 'socket.io'; import User from '@base/models/userModel'; -import IUser from '@base/types/user'; import AppError from '@base/errors/AppError'; import GroupChannel from '@base/models/groupChannelModel'; import deleteFile from '@base/utils/deleteFile'; +import { informSessions } from '@base/sockets/MessagingServices'; export const getLastMessage = async (chats: any) => { const lastMessages = await Promise.all( chats.map(async (chat: any) => { - const lastMessage = await Message.findOne({ chatId: chat.chat }).sort({ - timestamp: -1, - }); + const lastMessage = await Message.findOne({ chatId: chat.chat._id }).sort( + { + timestamp: -1, + } + ); return { chatId: chat.chat._id, lastMessage, @@ -23,6 +25,27 @@ export const getLastMessage = async (chats: any) => { return lastMessages; }; +export const getUnreadMessages = async (chats: any, user: any) => { + const mentionRegex = /@[[^]]+](([^)]+))/g; + return Promise.all( + chats.map(async (chat: any) => { + const unreadMessages = await Message.find({ + chatId: chat.chat._id, + senderId: { $ne: user._id }, + readBy: { $nin: [user._id] }, + }); + return { + chatId: chat.chat._id, + unreadMessagesCount: unreadMessages.length, + isMentioned: + unreadMessages.filter((message: any) => + mentionRegex.test(message.content) + ).length > 0, + }; + }) + ); +}; + export const getChats = async ( userId: mongoose.Types.ObjectId, type?: string @@ -55,25 +78,31 @@ export const enableDestruction = async ( if (chat && chat.destructionDuration) { setTimeout(async () => { await Message.findByIdAndDelete(messageId); - socket.to(chatId).emit('DESTRUCT_MESSAGE', messageId); + socket.to(chatId).emit('DELETE_MESSAGE_SERVER', messageId); }, chat.destructionDuration * 1000); } }; -export const unmute = async ( - user: IUser, +export const muteUnmuteChat = async ( + io: Server, + userId: string, chatId: string, - muteDuration: number + event: string, + muteDuration?: number ) => { - setTimeout(async () => { - user.chats.forEach((c: any) => { - if (c.chat.equals(chatId)) { - c.isMuted = false; - c.muteDuration = undefined; - } - }); - await user.save({ validateBeforeSave: false }); - }, muteDuration * 1000); + User.findByIdAndUpdate( + userId, + { + $set: { + 'chats.$[elem].isMuted': muteDuration, + 'chats.$[elem].muteDuration': muteDuration, + }, + }, + { + arrayFilters: [{ 'elem.chat': chatId }], + } + ); + informSessions(io, userId, { chatId }, event); }; export const deleteChatPictureFile = async ( diff --git a/src/services/googleAIService.ts b/src/services/googleAIService.ts new file mode 100644 index 0000000..b4af570 --- /dev/null +++ b/src/services/googleAIService.ts @@ -0,0 +1,41 @@ +const { HfInference } = require('@huggingface/inference'); + +const hf = new HfInference(process.env.HF_API_KEY); + +const modelName = 'unitary/toxic-bert'; + +async function detectInappropriateContent(text: string): Promise { + try { + const response = await hf.textClassification({ + model: modelName, + inputs: text, + }); + + console.log('Model Response:', JSON.stringify(response, null, 2)); + + const relevantLabels = ['toxic', 'obscene', 'insult', 'severe_toxic']; + const threshold = 0.7; + + interface TextClassificationResult { + label: string; + score: number; + } + + const toxicityScore = (response as TextClassificationResult[]) + .filter( + (result) => + relevantLabels.includes(result.label.toLowerCase()) && + result.score > threshold + ) + .reduce((acc, curr) => acc + curr.score, 0); + + console.log(`Total Toxicity Score: ${toxicityScore}`); + + return toxicityScore >= threshold; + } catch (error) { + console.error('Error detecting inappropriate content:', error); + throw new Error('Failed to detect inappropriate content'); + } +} + +export default detectInappropriateContent; diff --git a/src/sockets/MessagingServices.ts b/src/sockets/MessagingServices.ts new file mode 100644 index 0000000..e35c42d --- /dev/null +++ b/src/sockets/MessagingServices.ts @@ -0,0 +1,167 @@ +import { Server, Socket } from 'socket.io'; +import { Types } from 'mongoose'; +import { getChatIds } from '@services/chatService'; +import { getSocketsByUserId } from '@services/sessionService'; +import User from '@models/userModel'; +import GroupChannel from '@models/groupChannelModel'; +import Message from '@models/messageModel'; +import IMessage from '@base/types/message'; +import detectInappropriateContent from '@base/services/googleAIService'; + +export interface Member { + user: Types.ObjectId; + Role: 'member' | 'admin'; +} + +export const check = async ( + chat: any, + ack: Function, + senderId: any, + additionalData?: any +) => { + const { chatType, checkAdmin, newMessageIsReply, content, sendMessage } = + additionalData; + + if (!chat || chat.isDeleted) { + return ack({ + success: false, + message: 'Chat not found', + }); + } + + const chatMembers = chat.members; + if (chatMembers.length === 0) + return ack({ + success: false, + message: 'this chat is deleted and it no longer exists', + }); + + const sender: Member = chatMembers.find((m: Member) => + m.user.equals(senderId) + ) as unknown as Member; + + if (!sender) + return ack({ + success: false, + message: 'you are not a member of this chat', + }); + + if (chatType && !chatType.includes(chat.type)) + return ack({ + success: false, + message: `this is a ${chat.type} chat!`, + }); + + if (checkAdmin && sender.Role !== 'admin') + return ack({ + success: false, + message: 'you do not have permission as you are not an admin', + }); + + if (sendMessage && chat.type !== 'private') { + const groupChannelChat = await GroupChannel.findById(chat._id); + if ( + chat?.type === 'group' && + chat.isFilterd && + (await detectInappropriateContent(content)) + ) + return 'inappropriate'; + if (sender.Role !== 'admin') { + if (!groupChannelChat.messagingPermission) + return ack({ + success: false, + message: 'only admins can post and reply to this chat', + }); + if (chat.type === 'channel' && !newMessageIsReply) + return ack({ + success: false, + message: 'only admins can post to this channel', + }); + } + } + return 'ok'; +}; + +export const informSessions = async ( + io: Server, + userId: string, + data: any, + event: string +) => { + let memberSocket; + const socketIds = await getSocketsByUserId(userId); + if (!socketIds || socketIds.length !== 0) + socketIds.forEach((socketId: any) => { + memberSocket = io.sockets.sockets.get(socketId); + if (memberSocket) memberSocket.emit(event, data); + }); +}; + +export const joinRoom = async ( + io: Server, + roomId: String, + userId: Types.ObjectId +) => { + const socketIds = await getSocketsByUserId(userId); + socketIds.forEach(async (socketId: string) => { + const socket = io.sockets.sockets.get(socketId); + if (socket) socket.join(roomId.toString()); + }); +}; + +export const updateDraft = async ( + io: Server, + senderId: string, + chatId: string, + content: string +) => { + User.findByIdAndUpdate( + senderId, + { $set: { 'chats.$[chat].draft': content } }, + { + arrayFilters: [{ 'chat.chat': chatId }], + } + ); + informSessions( + io, + senderId, + { chatId, draft: content }, + 'UPDATE_DRAFT_SERVER' + ); +}; + +export const joinAllRooms = async (socket: Socket, userId: Types.ObjectId) => { + const chatIds = await getChatIds(userId); + chatIds.forEach(async (chatId: Types.ObjectId) => { + socket.join(chatId.toString()); + }); +}; + +export const deliverMessages = async ( + io: Server, + socket: Socket, + userId: Types.ObjectId +) => { + const user = await User.findById(userId); + if (!user) return; + + const messages = await Message.find({ + chatId: { $in: user.chats.map((chat: any) => chat.chat) }, + senderId: { $ne: userId }, + deliveredTo: { $nin: [userId] }, + readBy: { $nin: [userId] }, + }); + + Promise.all( + messages.map(async (message: IMessage) => { + message.deliveredTo.push(userId); + message.save(); + informSessions( + io, + message.senderId.toString(), + message, + 'MESSAGE_DELIVERED' + ); + }) + ); +}; diff --git a/src/sockets/chat.ts b/src/sockets/chat.ts deleted file mode 100644 index 38df82c..0000000 --- a/src/sockets/chat.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Server, Socket } from 'socket.io'; -import { updateDraft } from './services'; - -export const handleDraftMessage = async ( - io: Server, - socket: Socket, - data: any, - ack: Function, - senderId: string -) => { - const { chatId, content } = data; - await updateDraft(io, senderId, chatId, content); - ack({ success: true, message: 'Draft saved' }); -}; - -const registerChatHandlers = (io: Server, socket: Socket, userId: any) => { - socket.on('UPDATE_DRAFT_CLIENT', (data: any, ack: Function) => - handleDraftMessage(io, socket, data, ack, userId) - ); -}; - -export default registerChatHandlers; diff --git a/src/sockets/chats.ts b/src/sockets/chats.ts new file mode 100644 index 0000000..1faad14 --- /dev/null +++ b/src/sockets/chats.ts @@ -0,0 +1,586 @@ +import { Types } from 'mongoose'; +import { Server, Socket } from 'socket.io'; +import User from '@models/userModel'; +import Chat from '@models/chatModel'; +import GroupChannel from '@models/groupChannelModel'; +import NormalChat from '@base/models/normalChatModel'; +import { muteUnmuteChat } from '@base/services/chatService'; +import IUser from '@base/types/user'; +import { + check, + informSessions, + joinRoom, + updateDraft, +} from './MessagingServices'; + +const handleAddAdmins = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { members, chatId } = data; + const InvalidUsers: string[] = []; + + const chat = await Chat.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; + + await Promise.all( + members.map(async (memId: string) => { + const user = await User.findById(memId); + + if (!user) { + InvalidUsers.push(memId); + return; + } + + const isMemberOfChat = chat?.members.some((m) => m.user.equals(memId)); + + if (!isMemberOfChat) { + InvalidUsers.push(memId); + return; + } + + await Chat.findByIdAndUpdate( + chatId, + { $set: { 'members.$[elem].Role': 'admin' } }, + { + new: true, + arrayFilters: [{ 'elem.user': memId }], + } + ); + socket.to(chatId).emit('ADD_ADMINS_SERVER', { chatId, memId }); + }) + ); + + if (InvalidUsers.length > 0) { + return ack({ + success: false, + message: 'Some users could not be added as admins', + error: `Could not add users with IDs: ${InvalidUsers.join(', ')}`, + }); + } + + ack({ + success: true, + message: 'Added admins successfully', + data: {}, + }); +}; + +const handleAddMembers = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId, users } = data; + const invalidUsers: string[] = []; + + const chat = await Chat.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; + + if ( + chat?.type === 'group' && + chat.members.length + users.length > + parseInt(process.env.GROUP_SIZE ?? '10', 10) + ) + return ack({ + success: false, + message: 'Faild to create the chat', + error: `groups cannot have more than ${process.env.GROUP_SIZE ?? '10'} members`, + }); + + await Promise.all( + users.map(async (userId: any) => { + const user = await User.findById(userId); + + if (!user) { + invalidUsers.push(userId); + return; + } + + const isAlreadyMember = chat?.members.some((m: any) => + m.user.equals(userId) + ); + if (isAlreadyMember) { + invalidUsers.push(userId); + return; + } + + chat?.members.push({ user: userId, Role: 'member' }); + const userWasMember = user.chats.some((c: any) => c.chat.equals(chatId)); + if (!userWasMember) + await User.findByIdAndUpdate( + userId, + { $push: { chats: { chat: chatId } } }, + { new: true } + ); + + await joinRoom(io, chatId as string, userId); + socket.to(chatId).emit('ADD_MEMBERS_SERVER', { chatId, userId }); + }) + ); + + await chat?.save({ validateBeforeSave: false }); + + ack({ + success: true, + message: + invalidUsers.length > 0 + ? `Some users could not be added, IDs: ${invalidUsers.join(', ')}` + : 'Members added successfully', + data: {}, + }); +}; + +const handleCreatePrivateChat = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { memberId } = data; + const user = User.findById(senderId); + if (!user) + return ack({ + success: false, + message: 'Faild to create the chat', + error: 'you do not exsist!', + }); + + const newChat = new NormalChat({ + members: [ + { + user: memberId, + Role: 'member', + }, + { + user: senderId, + Role: 'member', + }, + ], + }); + + await newChat.save(); + await Promise.all([ + joinRoom(io, newChat._id as string, memberId), + joinRoom(io, newChat._id as string, senderId), + informSessions(io, memberId, newChat, 'JOIN_PRIVATE_CHAT'), + informSessions(io, senderId, newChat, 'JOIN_PRIVATE_CHAT'), + User.updateMany( + { _id: { $in: [memberId, senderId] } }, + { $push: { chats: { chat: newChat._id } } }, + { new: true } + ), + ]); + + ack({ + success: true, + message: 'Chat created successfuly', + data: newChat, + }); +}; + +const handleCreateGroupChannel = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { type, name, members } = data; + const user = User.findById(senderId); + if (!user) + return ack({ + success: false, + message: 'Faild to create the chat', + error: 'you need to login first', + }); + + if ( + type === 'group' && + members.length > parseInt(process.env.GROUP_SIZE ?? '10', 10) + ) + return ack({ + success: false, + message: 'Faild to create the chat', + error: `groups cannot have more than ${process.env.GROUP_SIZE ?? '10'} members`, + }); + + const membersWithRoles = members.map((id: Types.ObjectId) => ({ + user: id, + Role: 'member', + })); + const allMembers = [ + ...membersWithRoles, + { + user: senderId, + Role: 'admin', + }, + ]; + const newChat = new GroupChannel({ + name, + type, + members: allMembers, + }); + await newChat.save(); + await Promise.all([ + allMembers.map(async (member) => { + joinRoom(io, newChat._id as string, member.user); + informSessions(io, member.user, newChat, 'JOIN_GROUP_CHANNEL'); + }), + User.updateMany( + { _id: { $in: allMembers.map((member) => member.user) } }, + { $push: { chats: { chat: newChat._id } } }, + { new: true } + ), + ]); + + ack({ + success: true, + message: 'Chat created successfuly', + data: newChat, + }); +}; + +const handleDeleteGroupChannel = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId } = data; + + const chat = await GroupChannel.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; + + await User.updateMany( + { _id: { $in: chat.members.map((m: any) => m.user) } }, + { $pull: { chats: { chat: chatId } } } + ); + + socket.to(chatId).emit('DELETE_GROUP_CHANNEL_SERVER', { chatId }); + + chat.members = []; + chat.isDeleted = true; + await chat.save(); + + return ack({ + success: true, + message: 'chat deleted successfuly', + data: chatId, + }); +}; + +const handleLeaveGroupChannel = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId } = data; + const chat = await GroupChannel.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + }); + if (!func) return func; + + await Promise.all([ + GroupChannel.findByIdAndUpdate(chatId, { + $pull: { members: { user: senderId } }, + }), + User.findByIdAndUpdate(senderId, { + $pull: { chats: { chat: chatId } }, + }), + ]); + + socket + .to(chatId) + .emit('LEAVE_GROUP_CHANNEL_SERVER', { chatId, memberId: senderId }); + ack({ + success: true, + message: 'left the group successfuly', + data: {}, + }); +}; + +const handleRemoveMembers = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId, members } = data; + const invalidUsers: string[] = []; + + const chat = await GroupChannel.findById(chatId); + + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; + + await Promise.all( + members.map(async (memberId: any) => { + const user = await User.findById(memberId); + if (!user) { + invalidUsers.push(memberId); + return; + } + + const isMember = chat.members.some((m: any) => m.user.equals(memberId)); + if (!isMember) { + invalidUsers.push(memberId); + return; + } + + await Promise.all([ + GroupChannel.findByIdAndUpdate(chatId, { + $pull: { members: { user: memberId } }, + }), + User.findByIdAndUpdate(memberId, { + $pull: { chats: { chat: chatId } }, + }), + ]); + }) + ); + + socket + .to(chatId) + .emit('REMOVE_MEMBERS_SERVER', { chatId, memberId: senderId }); + + ack({ + success: true, + message: + invalidUsers.length > 0 + ? `Some users could not be removed, IDs: ${invalidUsers.join(', ')}` + : 'Members removed successfully', + data: {}, + }); +}; + +const handleSetPermission = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId, type, who } = data; + + if (!type || !who) + return ack({ + success: false, + message: 'could not update permissions', + error: 'missing required fields', + }); + + const chat = await GroupChannel.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; + + if (type === 'post') chat.messagingPermission = who === 'everyone'; + else if (type === 'download') chat.downloadingPermission = who === 'everyone'; + await chat.save(); + socket.to(chatId).emit('SET_PERMISSION_SERVER', { chatId, type, who }); + ack({ + success: true, + message: 'permissions updated successfully', + data: {}, + }); +}; + +const handleSetPrivacy = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId, privacy } = data; + + const chat = await GroupChannel.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; + + chat.privacy = privacy; + await chat.save(); + socket.to(chatId).emit('SET_PRIVACY_SERVER', { chatId, privacy }); + ack({ + success: true, + message: 'privacy updated successfully', + data: {}, + }); +}; + +const handleMuteChat = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { muteDuration, chatId } = data; + const userId = ((await User.findById(senderId)) as IUser) + ._id as Types.ObjectId; + + await muteUnmuteChat( + io, + userId.toString(), + chatId, + 'MUTE_CHAT', + muteDuration + ); + if (muteDuration !== -1) { + setTimeout(async () => { + muteUnmuteChat(io, userId.toString(), chatId, 'UNMUTE_CHAT'); + }, muteDuration * 1000); + } + + ack({ success: true, message: 'Chat muted successfully' }); +}; + +const handleUnMuteChat = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId } = data; + const userId = ((await User.findById(senderId)) as IUser) + ._id as Types.ObjectId; + await muteUnmuteChat(io, userId.toString(), chatId, 'UNMUTE_CHAT'); + ack({ success: true, message: 'Chat unmuted successfully' }); +}; + +const handleEnableDestruction = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { destructionDuration, chatId } = data; + const destructionTimestamp = Date.now(); + await NormalChat.findByIdAndUpdate(chatId, { + destructionDuration, + destructionTimestamp, + }); + await informSessions(io, senderId, { chatId }, 'ENABLE_DESTRUCTION_SERVER'); + ack({ success: true, message: 'Chat enabled destruction successfully' }); +}; + +const handleDisableDestruction = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: any +) => { + const { chatId } = data; + await NormalChat.findByIdAndUpdate(chatId, { + destructionTimestamp: undefined, + destructionDuration: undefined, + }); + await informSessions(io, senderId, { chatId }, 'ENABLE_DESTRUCTION_SERVER'); + ack({ success: true, message: 'Chat disabled destruction successfully' }); +}; + +const handleDraftMessage = async ( + io: Server, + socket: Socket, + data: any, + ack: Function, + senderId: string +) => { + const { chatId, content } = data; + await updateDraft(io, senderId, chatId, content); + ack({ success: true, message: 'Draft saved' }); +}; + +const registerChatHandlers = (io: Server, socket: Socket, userId: any) => { + socket.on('CREATE_PRIVATE_CHAT', (data: any, ack: Function) => { + handleCreatePrivateChat(io, socket, data, ack, userId); + }); + + socket.on('CREATE_GROUP_CHANNEL', (data: any, ack: Function) => { + handleCreateGroupChannel(io, socket, data, ack, userId); + }); + + socket.on('DELETE_GROUP_CHANNEL_CLIENT', (data: any, ack: Function) => { + handleDeleteGroupChannel(io, socket, data, ack, userId); + }); + + socket.on('LEAVE_GROUP_CHANNEL_CLIENT', (data: any, ack: Function) => { + handleLeaveGroupChannel(io, socket, data, ack, userId); + }); + + socket.on('SET_PERMISSION_CLIENT', (data: any, ack: Function) => { + handleSetPermission(io, socket, data, ack, userId); + }); + + socket.on('SET_PRIVACY_CLIENT', (data: any, ack: Function) => { + handleSetPrivacy(io, socket, data, ack, userId); + }); + + socket.on('ADD_ADMINS_CLIENT', (data: any, ack: Function) => { + handleAddAdmins(io, socket, data, ack, userId); + }); + + socket.on('ADD_MEMBERS_CLIENT', (data: any, ack: Function) => { + handleAddMembers(io, socket, data, ack, userId); + }); + + socket.on('REMOVE_MEMBERS_CLIENT', (data: any, ack: Function) => { + handleRemoveMembers(io, socket, data, ack, userId); + }); + + socket.on('MUTE_CHAT_CLIENT', (data: any, ack: Function) => { + handleMuteChat(io, socket, data, ack, userId); + }); + + socket.on('UNMUTE_CHAT_CLIENT', (data: any, ack: Function) => { + handleUnMuteChat(io, socket, data, ack, userId); + }); + + socket.on('ENABLE_DESTRUCTION_CLIENT', (data: any, ack: Function) => { + handleEnableDestruction(io, socket, data, ack, userId); + }); + + socket.on('DISABLE_DESTRUCTION_CLIENT', (data: any, ack: Function) => { + handleDisableDestruction(io, socket, data, ack, userId); + }); + + socket.on('UPDATE_DRAFT_CLIENT', (data: any, ack: Function) => + handleDraftMessage(io, socket, data, ack, userId) + ); +}; + +export default registerChatHandlers; diff --git a/src/sockets/messages.ts b/src/sockets/messages.ts index 7c86476..dce8126 100644 --- a/src/sockets/messages.ts +++ b/src/sockets/messages.ts @@ -1,57 +1,273 @@ -import Message from '@base/models/messageModel'; -import mongoose from 'mongoose'; +import { Types } from 'mongoose'; import { Server, Socket } from 'socket.io'; +import IMessage from '@base/types/message'; +import Message from '@models/messageModel'; +import { enableDestruction } from '@services/chatService'; +import Chat from '@base/models/chatModel'; +import { check, informSessions, updateDraft } from './MessagingServices'; +import handleNotifications from './notifications'; interface PinUnPinMessageData { - chatId: string | mongoose.Types.ObjectId; - messageId: string | mongoose.Types.ObjectId; + chatId: string | Types.ObjectId; + messageId: string | Types.ObjectId; } -async function handlePinMessage(socket: Socket, data: PinUnPinMessageData) { - try { - // Make a message pinned - const message = await Message.findById(data.messageId); - if (!message) { - //TODO: Make a global socket event for the client to send errors to - return; +const handleMessaging = async ( + io: any, + socket: Socket, + data: any, + ack: Function, + senderId: string +) => { + let { media, mediaName, mediaSize, content, contentType, parentMessageId } = + data; + const { chatId, chatType, isReply, isForward, isAnnouncement } = data; + + if ( + (!isForward && + !content && + !media && + (!contentType || !chatType || !chatId)) || + ((isReply || isForward) && !parentMessageId) + ) + return ack({ + success: false, + message: 'Failed to send the message', + error: 'missing required Fields', + }); + + if (isForward && (content || media || contentType)) + return ack({ + success: false, + message: 'Failed to send the message', + error: 'conflicting fields', + }); + + const chat = await Chat.findById(chatId); + const valid = await check(chat, ack, senderId, { + newMessageIsReply: isReply, + content, + sendMessage: true, + }); + if (!valid) return; + + let parentMessage; + if (isForward || isReply) { + parentMessage = (await Message.findById(parentMessageId)) as IMessage; + if (!parentMessage) + return ack({ + success: false, + message: 'Failed to send the message', + error: 'No message found with the provided id', + }); + + if (isForward) { + ({ content, contentType, media, mediaName, mediaSize } = parentMessage); + parentMessageId = undefined; } + } + + const message = new Message({ + media, + mediaName, + mediaSize, + content, + contentType, + isForward, + senderId, + chatId, + parentMessageId, + isAnnouncement, + isAppropriate: valid === 'ok', + }); - message.isPinned = true; - await message.save(); + await message.save(); - // Send an event to all online chat users to pin a message. - socket.to(data.chatId.toString()).emit('PIN_MESSAGE_SERVER', data); - } catch (err) { - //TODO: Make a global socket event for the client to send errors to + handleNotifications(message.id.toString()); + + if (parentMessage && isReply && chatType === 'channel') { + parentMessage.threadMessages.push(message._id as Types.ObjectId); + await parentMessage.save(); } -} -async function handleUnPinMessage(socket: Socket, data: PinUnPinMessageData) { - try { - // Make a message unpinned - const message = await Message.findById(data.messageId); - if (!message) { - //TODO: Make a global socket event for the client to send errors to - return; + await updateDraft(io, senderId, chatId, ''); + socket.to(chatId).emit('RECEIVE_MESSAGE', message, async (res: any) => { + if (res.success && res.userId !== senderId) { + if (res.isRead && !message.readBy.includes(res.userId)) { + message.readBy.push(res.userId); + } else if (!message.deliveredTo.includes(res.userId)) { + message.deliveredTo.push(res.userId); + } + message.save(); + informSessions( + io, + senderId, + message, + res.isRead ? 'MESSAGE_READ_SERVER' : 'MESSAGE_DELIVERED' + ); } + }); + enableDestruction(socket, message, chatId); + ack({ + success: true, + message: 'Message sent successfully', + data: message, + }); +}; + +const handleEditMessage = async (socket: Socket, data: any, ack: Function) => { + const { messageId, content, chatId } = data; + if (!messageId || !content) + return ack({ + success: false, + message: 'Failed to edit the message', + error: 'missing required Fields', + }); + const message = await Message.findByIdAndUpdate( + messageId, + { content, isEdited: true }, + { new: true } + ); + if (!message) + return ack({ + success: false, + message: 'Failed to edit the message', + error: 'no message found with the provided id', + }); + if (message.isForward) + return ack({ + success: false, + message: 'Failed to edit the message', + error: 'cannot edit a forwarded message', + }); + socket.to(chatId).emit('EDIT_MESSAGE_SERVER', message); + ack({ + success: true, + message: 'Message edited successfully', + res: { message }, + }); +}; + +const handleDeleteMessage = async ( + socket: Socket, + data: any, + ack: Function +) => { + const { messageId, chatId } = data; + if (!messageId) + return ack({ + success: false, + message: 'Failed to delete the message', + error: 'missing required Fields', + }); + const message = await Message.findByIdAndDelete(messageId); + if (!message) + return ack({ + success: false, + message: 'Failed to delete the message', + error: 'no message found with the provided id', + }); + socket.to(chatId).emit('DELETE_MESSAGE_SERVER', message); + ack({ success: true, message: 'Message deleted successfully' }); +}; - message.isPinned = false; - await message.save(); +const handleReadMessage = async ( + io: Server, + socket: Socket, + data: any, + ack: Function, + userId: string +) => { + const { chatId } = data; + const messages = await Message.find({ + chatId, + senderId: { $ne: userId }, + readBy: { $nin: [userId] }, + }); + if (!messages) + return ack({ + success: true, + message: 'No messages to read', + }); + messages.forEach(async (message: IMessage) => { + message.deliveredTo = message.deliveredTo.filter( + (id) => id.toString() !== userId + ); + message.readBy.push(new Types.ObjectId(userId)); + message.save(); + informSessions( + io, + message.senderId.toString(), + message, + 'MESSAGE_READ_SERVER' + ); + }); + ack({ success: true, message: 'Message read successfully' }); +}; - // Send an event to all online chat users to unpin a message. - socket.to(data.chatId.toString()).emit('UNPIN_MESSAGE_SERVER', data); - } catch (err) { - //TODO: Make a global socket event for the client to send errors to +const handlePinMessage = async ( + socket: Socket, + data: PinUnPinMessageData, + ack: Function +) => { + const message = await Message.findById(data.messageId); + if (!message) { + return ack({ success: false, message: 'Failed to pin message' }); } -} -async function registerMessagesHandlers(io: Server, socket: Socket, _userId: string) { - socket.on('PIN_MESSAGE_CLIENT', (data: PinUnPinMessageData) => - handlePinMessage(socket, data) + message.isPinned = true; + await message.save(); + + socket.to(data.chatId.toString()).emit('PIN_MESSAGE_SERVER', data); + ack({ success: true, message: 'Message pinned successfully' }); +}; + +const handleUnPinMessage = async ( + socket: Socket, + data: PinUnPinMessageData, + ack: Function +) => { + const message = await Message.findById(data.messageId); + if (!message) { + return ack({ success: false, message: 'Failed to unpin message' }); + } + + message.isPinned = false; + await message.save(); + + socket.to(data.chatId.toString()).emit('UNPIN_MESSAGE_SERVER', data); + ack({ success: true, message: 'Message unpinned successfully' }); +}; + +async function registerMessagesHandlers( + io: Server, + socket: Socket, + userId: string +) { + socket.on('SEND_MESSAGE', (data: any, ack: Function) => + handleMessaging(io, socket, data, ack, userId) + ); + + socket.on('EDIT_MESSAGE_CLIENT', (data: any, ack: Function) => + handleEditMessage(socket, data, ack) + ); + + socket.on('DELETE_MESSAGE_CLIENT', (data: any, ack: Function) => + handleDeleteMessage(socket, data, ack) + ); + + socket.on('MESSAGE_READ_CLIENT', (data: any, ack: Function) => { + handleReadMessage(io, socket, data, ack, userId); + }); + + socket.on('PIN_MESSAGE_CLIENT', (data: PinUnPinMessageData, ack: Function) => + handlePinMessage(socket, data, ack) ); - socket.on('UNPIN_MESSAGE_CLIENT', (data: PinUnPinMessageData) => - handleUnPinMessage(socket, data) + socket.on( + 'UNPIN_MESSAGE_CLIENT', + (data: PinUnPinMessageData, ack: Function) => + handleUnPinMessage(socket, data, ack) ); } diff --git a/src/sockets/notifications.ts b/src/sockets/notifications.ts new file mode 100644 index 0000000..fdd4bcc --- /dev/null +++ b/src/sockets/notifications.ts @@ -0,0 +1,56 @@ +import { messaging } from '@base/config/firebase'; +import Chat from '@base/models/chatModel'; +import Message from '@base/models/messageModel'; +import User from '@base/models/userModel'; + +const sendNotification = async (fcmToken: string, title: string, body: any) => { + const message = { + notification: { + title, + body, + }, + token: fcmToken, + }; + + try { + const response = await messaging.send(message); + console.log('Notification sent successfully:', response); + } catch (error) { + console.error('Error sending notification:', error); + } +}; + +const sendNotificationToChat = async (senderId: string, chatId: string) => { + const targetChat = await Chat.findById(chatId).populate('members'); + + if (!targetChat) return; + + const memberIds = targetChat.members.filter( + (memberId) => memberId.toString() !== senderId + ); + + const members = await User.find({ _id: { $in: memberIds } }, 'chats'); + + members.forEach((member) => { + const targetChatInfo = member.chats.find( + ({ chat, isMuted }) => chat.toString() === chatId.toString() && !isMuted + ); + + if (targetChatInfo) { + sendNotification( + member.fcmToken, + 'Message Received', + `Message received from ${member.username}` + ); + } + }); +}; + +const handleNotifications = async (messageId: string) => { + const message = await Message.findById(messageId); + + const { senderId, chatId } = message; + sendNotificationToChat(senderId, chatId); +}; + +export default handleNotifications; diff --git a/src/sockets/services.ts b/src/sockets/services.ts deleted file mode 100644 index 0ab904c..0000000 --- a/src/sockets/services.ts +++ /dev/null @@ -1,694 +0,0 @@ -import { Socket } from 'socket.io'; -import Message from '@base/models/messageModel'; -import { enableDestruction, getChatIds } from '@services/chatService'; -import IMessage from '@base/types/message'; -import NormalChat from '@base/models/normalChatModel'; -import mongoose, { ObjectId } from 'mongoose'; -import { getSocketsByUserId } from '@base/services/sessionService'; -import User from '@base/models/userModel'; -import Chat from '@base/models/chatModel'; -import GroupChannel from '@base/models/groupChannelModel'; - -interface Member { - _id: mongoose.Types.ObjectId; - Role: 'member' | 'admin'; -} - -const check = async (chatId: any, ack: Function, senderId: any) => { - if (!chatId) { - return ack({ success: false, message: 'provide the chatId' }); - } - const chat = await Chat.findById(chatId); - if (!chat) { - return ack({ - success: false, - message: 'no chat found with the provided id', - }); - } - - if (chat.type === 'private') - return ack({ - success: false, - message: 'this is a private chat!', - }); - const chatMembers = chat.members; - const chatMembersIds = chatMembers.map((m: any) => m._id); - if (chatMembersIds.length === 0) - return ack({ - success: false, - message: 'this chat is deleted and it no longer exists', - }); - - const admin: Member = chatMembers.find((m) => - m.user.equals(senderId) - ) as unknown as Member; - - if (!admin || admin.Role === 'member') - return ack({ - success: false, - message: 'you do not have permission', - }); -}; - -const inform = async (io: any, userId: string, data: any, event: string) => { - let memberSocket; - const socketIds = await getSocketsByUserId(userId); - if (!socketIds || socketIds.length !== 0) - socketIds.forEach((socketId: any) => { - memberSocket = io.sockets.sockets.get(socketId); - if (memberSocket) memberSocket.emit(event, data); - }); -}; - -const joinRoom = async (io: any, roomId: String, userId: ObjectId) => { - const socketIds = await getSocketsByUserId(userId); - socketIds.forEach((socketId: string) => { - const socket = io.sockets.sockets.get(socketId); - if (socket) socket.join(roomId); - }); -}; - -export const updateDraft = async ( - io: any, - senderId: string, - chatId: string, - content: string -) => { - await User.findByIdAndUpdate( - senderId, - { $set: { 'chats.$[chat].draft': content } }, - { - arrayFilters: [{ 'chat.chat': chatId }], - } - ); - await inform(io, senderId, { chatId, draft: content }, 'UPDATE_DRAFT_SERVER'); -}; - -export const joinAllRooms = async ( - socket: Socket, - userId: mongoose.Types.ObjectId -) => { - const chatIds = await getChatIds(userId); - chatIds.forEach((chatId: mongoose.Types.ObjectId) => { - socket.join(chatId.toString()); - }); -}; - -export const handleMessaging = async ( - io: any, - socket: Socket, - data: any, - ack: Function, - senderId: string -) => { - let { chatId, media, content, contentType, parentMessageId } = data; - const { isFirstTime, chatType, isReply, isForward } = data; - if ( - (!isForward && - !content && - !media && - (!contentType || !chatType || !chatId)) || - ((isReply || isForward) && !parentMessageId) - ) - return ack({ - success: false, - message: 'Failed to send the message', - error: 'missing required Fields', - }); - - if ( - (isFirstTime && isReply) || - (isForward && (content || media || contentType)) - ) - return ack({ - success: false, - message: 'Failed to send the message', - error: 'conflicting fields', - }); - if (isFirstTime) { - const members = [{ user: chatId }, { user: senderId }]; - const chat = new NormalChat({ members }); - const id = String(chat._id); - await chat.save(); - socket.join(id); - await joinRoom(io, id, chatId); - await User.findByIdAndUpdate(senderId, { - $push: { - chats: { chat: id }, - }, - }); - await User.findByIdAndUpdate(chatId, { - $push: { - chats: { chat: id }, - }, - }); - chatId = id; - } - - if (chatType !== 'private') { - const chat = await GroupChannel.findById(chatId); - if (!chat) - return ack({ - success: false, - message: 'Failed to send the message', - error: 'this chat does not exist', - }); - const sender = chat.members.find((member: any) => - member.user.equals(senderId) - ); - console.log(sender); - if (!sender) - return ack({ - success: false, - message: 'Failed to send the message', - error: 'you are not a member of this chat', - }); - if (sender.Role !== 'admin' && !chat.messagnigPermission) - return ack({ - success: false, - message: 'Failed to send the message', - error: 'only admins can post and reply to this chat', - }); - if (sender.Role !== 'admin' && !isReply && chatType === 'channel') - return ack({ - success: false, - message: 'Failed to send the message', - error: 'only admins can post to this channel', - }); - } - - let parentMessage; - if (isForward || isReply) { - parentMessage = (await Message.findById(parentMessageId)) as IMessage; - if (!parentMessage) - return ack({ - success: false, - message: 'Failed to send the message', - error: 'No message found with the provided id', - }); - - if (!parentMessage) - return ack({ - success: false, - message: 'Failed to reply to the message', - error: 'No message found with the provided parent message id', - }); - - if (isForward) { - ({ content, contentType, media } = parentMessage); - parentMessageId = undefined; - } - } - - const message = new Message({ - content, - contentType, - isForward, - senderId, - chatId, - parentMessageId, - messageType: chatType, - }); - await message.save(); - if (parentMessage && isReply && chatType === 'channel') { - parentMessage.threadMessages.push(message._id as mongoose.Types.ObjectId); - await parentMessage.save(); - } - - await updateDraft(io, senderId, chatId, ''); - socket.to(chatId).emit('RECEIVE_MESSAGE', message); - const res = { - messageId: message._id, - }; - enableDestruction(socket, message, chatId); - ack({ success: true, message: 'Message sent successfully', res }); -}; - -export const handleEditMessage = async ( - socket: Socket, - data: any, - ack: Function -) => { - const { messageId, content, chatId } = data; - if (!messageId || !content) - return ack({ - success: false, - message: 'Failed to edit the message', - error: 'missing required Fields', - }); - const message = await Message.findByIdAndUpdate( - messageId, - { content, isEdited: true }, - { new: true } - ); - if (!message) - return ack({ - success: false, - message: 'Failed to edit the message', - error: 'no message found with the provided id', - }); - if (message.isForward) - return ack({ - success: false, - message: 'Failed to edit the message', - error: 'cannot edit a forwarded message', - }); - socket.to(chatId).emit('EDIT_MESSAGE_SERVER', message); - ack({ - success: true, - message: 'Message edited successfully', - res: { message }, - }); -}; - -export const handleDeleteMessage = async ( - socket: Socket, - data: any, - ack: Function -) => { - const { messageId, chatId } = data; - if (!messageId) - return ack({ - success: false, - message: 'Failed to delete the message', - error: 'missing required Fields', - }); - const message = await Message.findByIdAndDelete(messageId); - if (!message) - return ack({ - success: false, - message: 'Failed to delete the message', - error: 'no message found with the provided id', - }); - socket.to(chatId).emit(messageId); - ack({ success: true, message: 'Message deleted successfully' }); -}; - -export const handleAddAdmins = async ( - io: any, - data: any, - ack: Function, - senderId: any -) => { - const { members, chatId } = data; - const forbiddenUsers: string[] = []; - const func = await check(chatId, ack, senderId); - const chat = await Chat.findById(chatId); - - if (func) return func; - - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'Could not add admins to the group', - error: 'Chat not found', - }); - - await Promise.all( - members.map(async (memId: string) => { - const user = await User.findById(memId); - - if (!user) { - forbiddenUsers.push(memId); - return; - } - - const isMemberOfChat = chat.members.some((m) => m.user.equals(memId)); - - if (!isMemberOfChat) { - forbiddenUsers.push(memId); - return; - } - - await Chat.findByIdAndUpdate( - chatId, - { $set: { 'members.$[elem].Role': 'admin' } }, - { - new: true, - arrayFilters: [{ 'elem.user': memId }], - } - ); - - await inform(io, memId, { chatId }, 'ADD_ADMINS_SERVER'); - }) - ); - - if (forbiddenUsers.length > 0) { - return ack({ - success: false, - message: 'Some users could not be added as admins', - error: `Could not add users with IDs: ${forbiddenUsers.join(', ')}`, - }); - } - - ack({ - success: true, - message: 'Added admins successfully', - data: {}, - }); -}; - -export const handleAddMembers = async ( - io: any, - data: any, - ack: Function, - senderId: any -) => { - const { chatId, users } = data; - const forbiddenUsers: string[] = []; - - const func = await check(chatId, ack, senderId); - if (func) return func; - - const chat = await Chat.findById(chatId); - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'No chat found with the provided ID', - }); - - await Promise.all( - users.map(async (userId: any) => { - const user = await User.findById(userId); - - if (!user) { - forbiddenUsers.push(userId); - return; - } - - const isAlreadyMember = chat.members.some((m: any) => - m.user.equals(userId) - ); - if (!isAlreadyMember) chat.members.push({ user: userId, Role: 'member' }); - const userWasMember = user.chats.some((c: any) => c.chat.equals(chatId)); - if (!userWasMember) user.chats.push(chatId); - console.log(user.chats); - if (isAlreadyMember) { - forbiddenUsers.push(userId); - return; - } - - await inform(io, userId, { chatId }, 'ADD_MEMBERS_SERVER'); - }) - ); - - if (forbiddenUsers.length > 0) { - return ack({ - success: false, - message: 'Some users could not be added', - error: `Could not add users with IDs: ${forbiddenUsers.join(', ')}`, - }); - } - - await chat.save(); - - ack({ - success: true, - message: 'Members added successfully', - data: {}, - }); -}; - -export const handleCreateGroupChannel = async ( - io: any, - socket: Socket, - data: any, - ack: Function, - senderId: any -) => { - const { type, name, members } = data; - const user = User.findById(senderId); - if (!user) - return ack({ - success: false, - message: 'Faild to create the chat', - error: 'you need to login first', - }); - - if (!process.env.GROUP_SIZE) - return ack({ - success: false, - message: 'Faild to create the chat', - error: 'define GROUP_SIZE in your .env file', - }); - - if (type === 'group' && members.length > process.env.GROUP_SIZE) - return ack({ - success: false, - message: 'Faild to create the chat', - error: `groups cannot have more than ${process.env.GROUP_SIZE} members`, - }); - - const membersWithRoles = members.map((id: ObjectId) => ({ - user: id, - Role: 'member', - })); - const allMembers = [ - ...membersWithRoles, - { - user: senderId, - Role: 'admin', - }, - ]; - const newChat = new GroupChannel({ - name, - type, - members: allMembers, - }); - await newChat.save(); - await Promise.all( - allMembers.map(async (member) => { - await joinRoom(io, newChat._id as string, member.user); - return User.findByIdAndUpdate( - member.user, - { $push: { chats: { chat: newChat._id } } }, - { new: true } - ); - }) - ); - socket - .to(newChat._id as string) - .emit('JOIN_GROUP_CHANNEL', { chatId: newChat._id as string }); - - ack({ - success: true, - message: 'Chat created successfuly', - data: newChat, - }); -}; - -export const handleDeleteGroupChannel = async ( - io: any, - socket: Socket, - data: any, - ack: Function, - senderId: any -) => { - const { chatId } = data; - const chat = await Chat.findById(chatId); - - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'Could not delete the group', - error: 'no chat found with the provided id', - }); - - const chatMembers = chat.members; - const isCreator = chatMembers.some( - (member) => member.user.toString() === senderId && member.Role === 'admin' - ); - - if (!isCreator) - return ack({ - success: false, - message: 'Could not delete the group', - error: 'you are not authorized to delete the group', - }); - - chatMembers.map(async (member: any) => { - await inform(io, member.user, { chatId }, 'DELETE_GROUP_CHANNEL_SERVER'); - }); - - chat.members = []; - chat.isDeleted = true; - await chat.save(); - - return ack({ - success: true, - message: 'chat deleted successfuly', - data: chatId, - }); -}; - -export const handleLeaveGroupChannel = async ( - io: any, - socket: Socket, - data: any, - ack: Function, - senderId: any -) => { - const { chatId } = data; - const chat = await Chat.findById(chatId); - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'could not leave the group', - error: 'this chat does no longer exist', - }); - const isMember = chat.members.some( - (member: any) => member.user.toString() === senderId.toString() - ); - if (!isMember) - return ack({ - success: false, - message: 'could not leave the group', - error: 'you are not a member of this chat', - }); - - await Chat.updateOne( - { _id: chatId }, - { $pull: { members: { user: senderId } } } - ); - - socket - .to(chatId) - .emit('LEAVE_GROUP_CHANNEL_SERVER', { chatId, memberId: senderId }); - ack({ - success: true, - message: 'left the group successfuly', - data: {}, - }); -}; - -export const handleRemoveMembers = async ( - io: any, - socket: Socket, - data: any, - ack: Function, - senderId: any -) => { - const { chatId, members } = data; - const forbiddenUsers: string[] = []; - - const chat = await Chat.findById(chatId); - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'could not remove members from the group', - error: 'this chat does no longer exist', - }); - - const admin: Member = chat.members.find((m) => - m.user.equals(senderId) - ) as unknown as Member; - - if (!admin) - return ack({ - success: false, - message: 'could not remove members from the group', - error: 'you are no longer a member of this group', - }); - - if (admin.Role === 'member') - return ack({ - success: false, - message: 'could not remove members from the group', - error: 'you do not have permission', - }); - - await Promise.all( - members.map(async (memberId: any) => { - const user = await User.findById(memberId); - if (!user) { - forbiddenUsers.push(memberId); - return; - } - - const isMember = chat.members.some((m: any) => m.user.equals(memberId)); - if (!isMember) { - forbiddenUsers.push(memberId); - return; - } - - await Chat.updateOne( - { _id: chatId }, - { $pull: { members: { user: memberId } } } - ); - - await inform(io, memberId, { chatId }, 'REMOVE_MEMBERS_SERVER'); - }) - ); - if (forbiddenUsers.length > 0) - return ack({ - success: false, - message: 'Some users could not be added', - error: `Could not remove users with IDs: ${forbiddenUsers.join(', ')}`, - }); - ack({ - success: true, - message: 'Members removed successfully', - data: {}, - }); -}; - -export const handleSetPermission = async ( - io: any, - socket: Socket, - data: any, - ack: Function, - senderId: any -) => { - const { chatId, type, who } = data; - - if (!type || !who) - return ack({ - success: false, - message: 'could not update permissions', - error: 'missing required fields', - }); - - const chat = await GroupChannel.findById(chatId); - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'could not update permissions', - error: 'this chat does no longer exist', - }); - - if (chat.type === 'private') - return ack({ - success: false, - message: 'could not update permissions', - error: 'cannot change permissions for private chats', - }); - - const admin: Member = chat.members.find((m: any) => - m.user.equals(senderId) - ) as unknown as Member; - - if (!admin) - return ack({ - success: false, - message: 'could not update permissions', - error: 'you are no longer a member of this group', - }); - - if (admin.Role === 'member') - return ack({ - success: false, - message: 'could not change group permissions', - error: 'you do not have permission', - }); - - if (type === 'post') chat.messagingPermission = who === 'everyone'; - else if (type === 'download') chat.downloadingPermission = who === 'everyone'; - await chat.save(); - socket.to(chatId).emit('SET_PERMISSION_SERVER', { chatId, type, who }); - console.log(chat); - ack({ - success: true, - message: 'permissions updated successfully', - data: {}, - }); -}; diff --git a/src/sockets/socket.ts b/src/sockets/socket.ts index a851bc9..3c9f06d 100644 --- a/src/sockets/socket.ts +++ b/src/sockets/socket.ts @@ -1,24 +1,13 @@ import { Server } from 'socket.io'; import { Server as HTTPServer } from 'http'; import corsOptions from '@base/config/cors'; -import registerChatHandlers from '@base/sockets/chat'; +import registerChatHandlers from '@base/sockets/chats'; import redisClient from '@base/config/redis'; -import mongoose from 'mongoose'; -import { - joinAllRooms, - handleEditMessage, - handleDeleteMessage, - handleMessaging, - handleAddAdmins, - handleCreateGroupChannel, - handleDeleteGroupChannel, - handleLeaveGroupChannel, - handleAddMembers, - handleRemoveMembers, - handleSetPermission, -} from './services'; +import { Types } from 'mongoose'; +import { deliverMessages, joinAllRooms } from './MessagingServices'; import registerMessagesHandlers from './messages'; import { authorizeSocket, protectSocket } from './middlewares'; +import registerVoiceCallHandlers from './voiceCalls'; const socketSetup = (server: HTTPServer) => { const io = new Server(server, { @@ -29,53 +18,10 @@ const socketSetup = (server: HTTPServer) => { io.use(protectSocket); io.on('connection', async (socket: any) => { - const userId = socket.request.session.user.id; + const userId = socket.request.session.user.id as string; console.log(`New client with userID ${userId} connected: ${socket.id}`); - await joinAllRooms(socket, new mongoose.Types.ObjectId(userId as string)); - - socket.on('SEND_MESSAGE', (data: any, ack: Function) => - handleMessaging(io, socket, data, ack, userId) - ); - - socket.on('EDIT_MESSAGE_CLIENT', (data: any, ack: Function) => - handleEditMessage(socket, data, ack) - ); - - socket.on('DELETE_MESSAGE', (data: any, ack: Function) => - handleDeleteMessage(socket, data, ack) - ); - - socket.on('ADD_ADMINS_CLIENT', (data: any, ack: Function) => { - handleAddAdmins(io, data, ack, userId); - }); - - socket.on('ADD_MEMBERS_CLIENT', (data: any, ack: Function) => { - handleAddMembers(io, data, ack, userId); - }); - - socket.on('CREATE_GROUP_CHANNEL', (data: any, ack: Function) => { - handleCreateGroupChannel(io, socket, data, ack, userId); - }); - - socket.on('DELETE_GROUP_CHANNEL_CLIENT', (data: any, ack: Function) => { - handleDeleteGroupChannel(io, socket, data, ack, userId); - }); - - socket.on('LEAVE_GROUP_CHANNEL_CLIENT', (data: any, ack: Function) => { - handleLeaveGroupChannel(io, socket, data, ack, userId); - }); - - socket.on('REMOVE_MEMBERS_CLIENT', (data: any, ack: Function) => { - handleRemoveMembers(io, socket, data, ack, userId); - }); - - socket.on('REMOVE_MEMBERS_CLIENT', (data: any, ack: Function) => { - handleRemoveMembers(io, socket, data, ack, userId); - }); - - socket.on('SET_PERMISSION_CLIENT', (data: any, ack: Function) => { - handleSetPermission(io, socket, data, ack, userId); - }); + await joinAllRooms(socket, new Types.ObjectId(userId)); + deliverMessages(io, socket, new Types.ObjectId(userId)); socket.on('error', (error: Error) => { console.error(`Socket error on ${socket.id}:`, error); @@ -91,11 +37,12 @@ const socketSetup = (server: HTTPServer) => { socket.request.session.user.lastSeenTime = Date.now(); socket.request.session.user.status = 'offline'; socket.request.session.save(); - await redisClient.sRem(`user:${userId}:sockets`, socket.id); + redisClient.sRem(`user:${userId}:sockets`, socket.id); }); registerChatHandlers(io, socket, userId); registerMessagesHandlers(io, socket, userId); + registerVoiceCallHandlers(io, socket, userId); }); }; diff --git a/src/sockets/voiceCalls.ts b/src/sockets/voiceCalls.ts new file mode 100644 index 0000000..22db3c8 --- /dev/null +++ b/src/sockets/voiceCalls.ts @@ -0,0 +1,135 @@ +import { Server, Socket } from 'socket.io'; +import { + addClientToCall, + createVoiceCall, + getClientSocketId, + removeClientFromCall, +} from './voiceCallsServices'; + +interface CreateCallData { + chatId: string; + targetId: string | undefined; +} + +interface JoinLeaveCallData { + voiceCallId: string; +} + +interface SignalData { + type: 'ICE' | 'OFFER' | 'ANSWER'; + targetId: string; + voiceCallId: string; + data: any; +} + +async function handleCreateCall( + io: Server, + socket: Socket, + data: CreateCallData, + userId: string +) { + const { targetId } = data; + let { chatId } = data; + + if (targetId && !chatId) { + //TODO: Create a new chat between the target and the user. + chatId = '123'; + } + + console.log('User Started Call: ', userId); + const voiceCall = await createVoiceCall(chatId, userId); + + io.to(chatId).emit('CALL-STARTED', { + snederId: userId, + chatId, + voiceCallId: voiceCall._id, + }); +} + +async function handleJoinCall( + io: Server, + socket: Socket, + data: JoinLeaveCallData, + userId: string +) { + const { voiceCallId } = data; + console.log( + `Client Joined call, clientId: ${userId} , callId: ${voiceCallId}` + ); + await addClientToCall(socket, userId, voiceCallId); + + socket.join(voiceCallId); + + socket.to(voiceCallId).emit('CLIENT-JOINED', { + clientId: userId, + voiceCallId, + }); +} + +async function handleSignal( + io: Server, + socket: Socket, + signalData: SignalData, + userId: string +) { + const { type, targetId, voiceCallId, data } = signalData; + + console.log( + `Signal Sent, type: ${type}, senderId: ${userId}, targetId: ${targetId}, voiceCallId: ${voiceCallId}` + ); + + const socketId = getClientSocketId(voiceCallId, targetId); + + io.to(socketId).emit('SIGNAL-CLIENT', { + type, + senderId: userId, + voiceCallId, + data, + }); +} + +async function handleLeaveCall( + io: Server, + socket: Socket, + data: JoinLeaveCallData, + userId: string +) { + const { voiceCallId } = data; + + console.log(`Client Left, clientId: ${userId}, voiceCallId: ${voiceCallId}`); + + socket.leave(voiceCallId); + + await removeClientFromCall(userId, voiceCallId); + + socket.to(voiceCallId).emit('CLIENT-LEFT', { + clientId: userId, + voiceCallId, + }); +} + +async function registerVoiceCallHandlers( + io: Server, + socket: Socket, + userId: string +) { + socket.on('CREATE-CALL', (data: CreateCallData) => { + handleCreateCall(io, socket, data, userId.toString()); + }); + + socket.on('JOIN-CALL', (data: JoinLeaveCallData) => { + handleJoinCall(io, socket, data, userId.toString()); + }); + + socket.on('SIGNAL-SERVER', (data: SignalData) => { + handleSignal(io, socket, data, userId.toString()); + }); + + socket.on('LEAVE', (data: JoinLeaveCallData) => { + handleLeaveCall(io, socket, data, userId.toString()); + }); + + //TODO: DON'T FORGET TO HANDLE ERRORS (WRAP HANDLERS WITH ANOTHER FUNCTION LIKE catchAsync) +} + +export default registerVoiceCallHandlers; diff --git a/src/sockets/voiceCallsServices.ts b/src/sockets/voiceCallsServices.ts new file mode 100644 index 0000000..7570d57 --- /dev/null +++ b/src/sockets/voiceCallsServices.ts @@ -0,0 +1,118 @@ +import Chat from '@base/models/chatModel'; +import VoiceCall from '@base/models/voiceCallModel'; +import IVoiceCall from '@base/types/voiceCall'; +import mongoose from 'mongoose'; +import { Socket } from 'socket.io'; + +interface ClientSocketMap { + [voiceCallId: string]: { + [userId: string]: string; + }; +} + +const clientSocketMap: ClientSocketMap = {}; + +async function endVoiceCall(voiceCallId: string) { + // Delete voice call entry in map. + delete clientSocketMap[voiceCallId]; + + const voiceCall: IVoiceCall = await VoiceCall.findById(voiceCallId); + + // Calculate duration in minutes + voiceCall.duration = Math.floor( + (Date.now() - voiceCall.timestamp.getTime()) / (1000 * 60) + ); + + voiceCall.status = 'finished'; + + await voiceCall.save(); +} + +export async function createVoiceCall(chatId: string, userId: string) { + const chat = await Chat.findById(chatId); + + const voiceCall = new VoiceCall({ + callType: chat?.type === 'private' ? 'private' : 'group', + senderId: new mongoose.Types.ObjectId(userId), + chatId: new mongoose.Types.ObjectId(chatId), + }); + + await voiceCall.save(); + + console.log('Voice Call created: ', voiceCall._id); + + return voiceCall; +} + +export async function addClientToCall( + socket: Socket, + userId: string, + voiceCallId: string +) { + // Add a user Id object into the call current participants. + const voiceCall: IVoiceCall = await VoiceCall.findById(voiceCallId); + const userIdObj = new mongoose.Types.ObjectId(userId); + + //TODO: UNCOMMENT AFTER IMPLEMENTING ERROR HANDLING + /* + if (voiceCall.status === 'finished') { + throw new Error('This voice call has already finished!'); + } + */ + + const userIdIndex = voiceCall.currentParticipants.indexOf(userIdObj); + + if (userIdIndex === -1) { + voiceCall.currentParticipants.push(userIdObj); + } else { + voiceCall.currentParticipants[userIdIndex] = userIdObj; + } + + await voiceCall.save(); + + // Add the client socket id into the map + if (!clientSocketMap[voiceCallId]) clientSocketMap[voiceCallId] = {}; + clientSocketMap[voiceCallId][userId] = socket.id; + console.log('clientSocketMap: ', clientSocketMap[voiceCallId]); +} + +export async function removeClientFromCall( + userId: string, + voiceCallId: string +) { + // Delete the userId entry from the map + if (clientSocketMap[voiceCallId]) delete clientSocketMap[voiceCallId][userId]; + + // Delete the userId from current participants of voice call. + const voiceCall: IVoiceCall = await VoiceCall.findById(voiceCallId); + const userIdObj = new mongoose.Types.ObjectId(userId); + + const userIdIndex = voiceCall.currentParticipants.indexOf(userIdObj); + + if (userIdIndex !== -1) { + voiceCall.currentParticipants.splice(userIdIndex, 1); + await voiceCall.save(); + } + + if (voiceCall.currentParticipants.length === 0) { + await endVoiceCall(voiceCallId); + } + + console.log('clientSocketMap: ', clientSocketMap[voiceCallId]); +} + +export function getClientSocketMap(): ClientSocketMap { + return clientSocketMap; +} + +export function getClientSocketId(voiceCallId: string, userId: string) { + //TODO: UNCOMMENT AFTER IMPLEMENTING ERROR HANDLING + /* + if (!clientSocketMap[voiceCallId]) + throw new Error('No voice call exists with this id!'); + + if (!clientSocketMap[voiceCallId][userId]) + throw new Error('No socket exists for this user id!'); + */ + return clientSocketMap[voiceCallId][userId]; +} diff --git a/src/types/message.ts b/src/types/message.ts index 00f8289..470dd96 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -2,19 +2,20 @@ import { Types } from 'mongoose'; import ICommunication from './communication'; interface IMessage extends ICommunication { - timestamp: Date; - media: string; content: string; + media: string; + mediaName: string; + mediaSize: number; contentType: string; isPinned: boolean; isForward: boolean; isEdited: boolean; isAnnouncement: boolean; - senderId: Types.ObjectId; - chatId: Types.ObjectId; + deliveredTo: Types.ObjectId[]; + readBy: Types.ObjectId[]; parentMessageId: Types.ObjectId | undefined; threadMessages: Types.ObjectId[]; - messageType: string; + isAppropriate: boolean; } export default IMessage; diff --git a/src/types/normalChat.ts b/src/types/normalChat.ts index d1de112..66b0955 100644 --- a/src/types/normalChat.ts +++ b/src/types/normalChat.ts @@ -1,6 +1,10 @@ import IChat from './chat'; interface INormalChat extends IChat { + encryptionKey: String; + initializationVector: String; + keyAuthTag: String; + vectorAuthTag: String; destructionTimestamp: Date | undefined; destructionDuration: number | undefined; } diff --git a/src/types/user.ts b/src/types/user.ts index 5f02951..fd89b08 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -7,6 +7,7 @@ interface IUser extends Document { screenFirstName: string; screenLastName: string; email: string; + fcmToken: string; phoneNumber: string | undefined; password: string | undefined; passwordConfirm: string | undefined; @@ -43,7 +44,6 @@ interface IUser extends Document { isCorrectPassword: (_candidatePass: string) => Promise; passwordChanged: (_tokenIssuedAt: number) => boolean; createResetPasswordToken: () => string; - selectFields: () => void; } export default IUser; diff --git a/src/types/voiceCall.ts b/src/types/voiceCall.ts index d8c0776..62babb2 100644 --- a/src/types/voiceCall.ts +++ b/src/types/voiceCall.ts @@ -1,7 +1,10 @@ +import { Types } from 'mongoose'; import ICommunication from './communication'; interface IVoiceCall extends ICommunication { - duration: number; + callType: String; + currentParticipants: Types.ObjectId[]; + duration: Number; status: String; } diff --git a/src/utils/email.ts b/src/utils/email.ts index d96dcae..356ff62 100644 --- a/src/utils/email.ts +++ b/src/utils/email.ts @@ -3,7 +3,7 @@ import { MailOptions } from 'nodemailer/lib/json-transport'; const telwareTeam: string = 'Telware '; -const createTransporter = (provider: string) => { +const createTransporter = (provider?: string) => { if (provider === 'gmail') return nodemailer.createTransport({ service: 'gmail', @@ -14,17 +14,17 @@ const createTransporter = (provider: string) => { }); return nodemailer.createTransport({ - host: process.env.GMAIL_HOST, + host: process.env.MAILTRAP_HOST, port: Number(process.env.MAIL_PORT), auth: { - user: process.env.TELWARE_EMAIL, - pass: process.env.TELWARE_PASSWORD, + user: process.env.MAILTRAP_USERNAME, + pass: process.env.MAILTRAP_PASSWORD, }, }); }; const sendEmail = async (options: any) => { - const transporter = createTransporter('gmail'); + const transporter = createTransporter(process.env.EMAIL_PROVIDER); const mailOptions: MailOptions = { from: telwareTeam, diff --git a/src/utils/encryption.ts b/src/utils/encryption.ts new file mode 100644 index 0000000..bef0abd --- /dev/null +++ b/src/utils/encryption.ts @@ -0,0 +1,42 @@ +import crypto from 'crypto'; + +export const encryptKey = (key: String) => { + const cipher = crypto.createCipheriv( + 'aes-256-gcm', + Buffer.from(process.env.ENCRYPTION_KEY_SECRET as string, 'hex'), + Buffer.from(process.env.ENCRYPTION_KEY_IV as string, 'hex') + ); + + const encrypted = Buffer.concat([ + cipher.update(Buffer.from(key, 'hex')), + cipher.final(), + ]).toString('hex'); + return { encrypted, authTag: cipher.getAuthTag().toString('hex') }; +}; + +export const decryptKey = (key: String, authTag: String) => { + const decipher = crypto.createDecipheriv( + 'aes-256-gcm', + Buffer.from(process.env.ENCRYPTION_KEY_SECRET as string, 'hex'), + Buffer.from(process.env.ENCRYPTION_KEY_IV as string, 'hex') + ); + decipher.setAuthTag(Buffer.from(authTag, 'hex')); + + return Buffer.concat([ + decipher.update(Buffer.from(key, 'hex')), + decipher.final(), + ]).toString('hex'); +}; + +export const encryptMessage = (message: String, key: String, iv: String) => { + const cipher = crypto.createCipheriv( + 'aes-256-cbc', + Buffer.from(key, 'hex'), + Buffer.from(iv, 'hex') + ); + + return Buffer.concat([ + cipher.update(Buffer.from(message)), + cipher.final(), + ]).toString('hex'); +};