diff --git a/packages/analytics/package.json b/packages/analytics/package.json index d035121..04928f5 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -13,7 +13,8 @@ "@linux-odyssey/models": "*", "express": "^4.18.2", "express-basic-auth": "^1.2.1", - "express-handlebars": "^7.1.2" + "express-handlebars": "^7.1.2", + "mongoose": "^8.0.3" }, "devDependencies": { "nodemon": "^3.0.1" diff --git a/packages/analytics/src/constants.js b/packages/analytics/src/constants.js new file mode 100644 index 0000000..e69de29 diff --git a/packages/analytics/src/controllers/commands.js b/packages/analytics/src/controllers/commands.js index 4692652..b570504 100644 --- a/packages/analytics/src/controllers/commands.js +++ b/packages/analytics/src/controllers/commands.js @@ -1,9 +1,24 @@ +/* eslint-disable object-shorthand */ +import Pagination from '../models/pagination.js' import { errorCommands } from '../models/commands.js' // eslint-disable-next-line import/prefer-default-export export async function commandListController(req, res) { - const payload = { - errorCommands: await errorCommands(), + const { nextKey, order } = req.query + const itemsPerPage = 100 + + try { + const pagination = new Pagination(order, itemsPerPage, nextKey) + const payload = await errorCommands(pagination) + const newNextKey = payload[payload.length - 1]?._id + + res.render('commands', { + errorCommands: payload, + nextKey: newNextKey, + order: pagination.getOrder(), + }) + } catch (error) { + console.error(error) + res.status(500).send('Error fetching data') } - res.render('commands', payload) } diff --git a/packages/analytics/src/controllers/index.js b/packages/analytics/src/controllers/index.js index d5a6967..19edb1d 100644 --- a/packages/analytics/src/controllers/index.js +++ b/packages/analytics/src/controllers/index.js @@ -1,15 +1,24 @@ import { userCount } from '../models/users.js' -import { sessionCount, sessionCompleted } from '../models/sessions.js' +import { questList } from '../models/quests.js' // eslint-disable-next-line import/prefer-default-export export async function homeController(req, res) { + const quests = await questList() + const [totalQuests, totalCompleted] = quests.reduce( + ([total, completed], quest) => [ + total + quest.started, + completed + quest.completed, + ], + [0, 0] + ) + const payload = { title: 'Linux Odyssey Admin', userCount: await userCount(), - sessionCount: await sessionCount(), - sessionCompleted: await sessionCompleted(), + totalQuests, + totalCompleted, } payload.completedRate = - Math.round((payload.sessionCompleted / payload.sessionCount) * 10000) / 100 + Math.round((payload.totalCompleted / payload.totalQuests) * 10000) / 100 res.render('home', payload) } diff --git a/packages/analytics/src/controllers/quests.js b/packages/analytics/src/controllers/quests.js new file mode 100644 index 0000000..4e5b12a --- /dev/null +++ b/packages/analytics/src/controllers/quests.js @@ -0,0 +1,10 @@ +import { questList } from '../models/quests.js' + +// eslint-disable-next-line import/prefer-default-export +export async function questListController(req, res) { + const quests = await questList() + quests.forEach((quest) => { + quest.failed = quest.started - quest.completed + }) + res.render('quests', { quests }) +} diff --git a/packages/analytics/src/controllers/sessions.js b/packages/analytics/src/controllers/sessions.js index 47dc3bb..a4dbe89 100644 --- a/packages/analytics/src/controllers/sessions.js +++ b/packages/analytics/src/controllers/sessions.js @@ -1,8 +1,24 @@ +import Pagination from '../models/pagination.js' import { sessionList, sessionDetail } from '../models/sessions.js' export async function sessionListController(req, res) { - const sessions = await sessionList() - res.render('sessions', { sessions }) + const { nextKey, order } = req.query + const itemsPerPage = 50 + + try { + const pagination = new Pagination(order, itemsPerPage, nextKey) + const sessions = await sessionList(pagination) + const newNextKey = sessions[sessions.length - 1]?._id + + res.render('sessions', { + sessions, + nextKey: newNextKey, + order: pagination.getOrder(), + }) + } catch (error) { + console.log(error) + res.status(500).send('Error fetching session data') + } } export async function sessionDetailController(req, res) { diff --git a/packages/analytics/src/controllers/users.js b/packages/analytics/src/controllers/users.js index 5eb16b9..220bb3b 100644 --- a/packages/analytics/src/controllers/users.js +++ b/packages/analytics/src/controllers/users.js @@ -1,6 +1,22 @@ +import Pagination from '../models/pagination.js' import { userList } from '../models/users.js' -// eslint-disable-next-line import/prefer-default-export export async function userListController(req, res) { - res.render('users', { users: await userList() }) + const { nextKey, order } = req.query + const itemsPerPage = 50 + + try { + const pagination = new Pagination(order, itemsPerPage, nextKey) + const users = await userList(pagination) + const newNextKey = users[users.length - 1]?._id + + res.render('users', { + users, + nextKey: newNextKey, + order: pagination.getOrder(), + }) + } catch (error) { + console.error(error) + res.status(500).send('Error fetching user data') + } } diff --git a/packages/analytics/src/models/commands.js b/packages/analytics/src/models/commands.js index 48ece73..e771c4f 100644 --- a/packages/analytics/src/models/commands.js +++ b/packages/analytics/src/models/commands.js @@ -1,22 +1,44 @@ import { Command } from '@linux-odyssey/models' // eslint-disable-next-line import/prefer-default-export -export async function errorCommands() { - const commands = await Command.find({ - $and: [ - { error: { $exists: true } }, - { error: { $ne: null } }, - { error: { $ne: '' } }, - ], - }) - .populate({ - path: 'session', - populate: { path: 'user' }, - }) - .exec() - return commands.map(({ command, error, createdAt, session }) => { +export async function errorCommands(pagination) { + const commands = await Command.aggregate([ + pagination.match('_id'), + { + $match: { + $and: [ + { error: { $exists: true } }, + { error: { $ne: null } }, + { error: { $ne: '' } }, + ], + }, + }, + { + $lookup: { + from: 'sessions', // 替換成 'session' 對應的集合名稱 + localField: 'session', // 替換成 'session' 對應的字段名稱 + foreignField: '_id', + as: 'session', + }, + }, + { $unwind: '$session' }, + { + $lookup: { + from: 'users', // 替換成 'user' 對應的集合名稱 + localField: 'session.user', // 替換成 'user' 對應的字段名稱 + foreignField: '_id', + as: 'session.user', + }, + }, + { $unwind: '$session.user' }, + pagination.sort('_id'), + pagination.limit(), + ]) + + return commands.map(({ _id, command, error, createdAt, session }) => { const { user, quest } = session return { + _id, command: command.slice(0, 20), error: error.slice(0, 50), createdAt: createdAt?.toLocaleString(), diff --git a/packages/analytics/src/models/pagination.js b/packages/analytics/src/models/pagination.js new file mode 100644 index 0000000..255d9fd --- /dev/null +++ b/packages/analytics/src/models/pagination.js @@ -0,0 +1,74 @@ +/* eslint-disable lines-between-class-members */ +/* eslint-disable class-methods-use-this */ +// eslint-disable-next-line max-classes-per-file +import mongoose from 'mongoose' + +export const DESC = 'desc' +export const ASC = 'asc' + +class Asc { + getOperator() { + return '$gt' + } + + getOrder() { + return 1 + } + + getName() { + return ASC + } +} + +class Desc { + getOperator() { + return '$lt' + } + + getOrder() { + return -1 + } + + getName() { + return DESC + } +} + +export default class Pagination { + nextKey = null + itemsPerPage = 50 + order = null + + constructor(order, itemsPerPage = 50, nextKey = null) { + this.itemsPerPage = itemsPerPage + if (!order || order === DESC) { + this.order = new Desc() + } else if (order === ASC) { + this.order = new Asc() + } else { + throw new Error('order must be asc or desc') + } + if (nextKey) { + this.nextKey = new mongoose.Types.ObjectId(nextKey) + } + } + + match(field) { + const stage = this.nextKey + ? { [field]: { [this.order.getOperator()]: this.nextKey } } + : {} + return { $match: stage } + } + + sort(field) { + return { $sort: { [field]: this.order.getOrder() } } + } + + limit() { + return { $limit: this.itemsPerPage } + } + + getOrder() { + return this.order.getName() + } +} diff --git a/packages/analytics/src/models/quests.js b/packages/analytics/src/models/quests.js new file mode 100644 index 0000000..2b64889 --- /dev/null +++ b/packages/analytics/src/models/quests.js @@ -0,0 +1,59 @@ +/* eslint-disable object-shorthand */ +/* eslint-disable func-names */ +import { UserProfile } from '@linux-odyssey/models' + +export function questList() { + return UserProfile.aggregate([ + { + $project: { + progress: { + $objectToArray: '$progress', + }, + }, + }, + { + $unwind: '$progress', + }, + { + $group: { + _id: '$progress.v.quest', + started: { + $sum: 1, + }, + completed: { + $sum: { + $cond: { + if: { $eq: ['$progress.v.completed', true] }, + then: 1, + else: 0, + }, + }, + }, + }, + }, + { + $project: { + _id: 1, + started: 1, + completed: 1, + completedRate: { + $cond: { + if: { $eq: ['$started', 0] }, + then: 0, + else: { + $round: [ + { $multiply: [{ $divide: ['$completed', '$started'] }, 100] }, + 0, + ], + }, + }, + }, + }, + }, + { + $sort: { + completed: -1, + }, + }, + ]) +} diff --git a/packages/analytics/src/models/sessions.js b/packages/analytics/src/models/sessions.js index 72d5aa4..f7b4485 100644 --- a/packages/analytics/src/models/sessions.js +++ b/packages/analytics/src/models/sessions.js @@ -1,28 +1,5 @@ import { Command, Session } from '@linux-odyssey/models' -export function sessionCount() { - return Session.find().count() -} - -export function sessionCompleted() { - return Session.find({ status: 'finished' }).count() -} - -export function averageTimeUsage() { - return Session.aggregate([ - { - $group: { - _id: '$quest', - average: { - $avg: { - $subtract: ['$finishedAt', '$createdAt'], - }, - }, - }, - }, - ]) -} - function formatTime(time) { const seconds = Math.floor(time / 1000) const minutes = Math.floor(seconds / 60) @@ -30,8 +7,9 @@ function formatTime(time) { return `${hours}h ${minutes % 60}m ${seconds % 60}s` } -export async function sessionList() { +export async function sessionList(pagination) { const sessions = await Session.aggregate([ + pagination.match('_id'), { $lookup: { from: 'users', @@ -49,7 +27,10 @@ export async function sessionList() { as: 'commands', }, }, + pagination.sort('_id'), + pagination.limit(), ]) + return sessions.map( ({ _id, diff --git a/packages/analytics/src/models/users.js b/packages/analytics/src/models/users.js index 69d8310..896fe5d 100644 --- a/packages/analytics/src/models/users.js +++ b/packages/analytics/src/models/users.js @@ -11,8 +11,9 @@ export function userCount() { return User.find().count() } -export async function userList() { +export async function userList(pagination) { const users = await Session.aggregate([ + pagination.match('user'), { $group: { _id: '$user', @@ -29,11 +30,14 @@ export async function userList() { }, }, { $unwind: '$userData' }, - { $sort: { 'userData.createdAt': 1 } }, + pagination.sort('userData._id'), + pagination.limit(), ]) + return users.map(({ userData, count, lastActivityAt }) => { - const { username, email, createdAt } = userData + const { _id, username, email, createdAt } = userData return { + _id, username, email, createdAt: createdAt?.toLocaleString(), diff --git a/packages/analytics/src/router.js b/packages/analytics/src/router.js index f5a458b..9ef6ba6 100644 --- a/packages/analytics/src/router.js +++ b/packages/analytics/src/router.js @@ -2,6 +2,7 @@ import { Router } from 'express' import { homeController } from './controllers/index.js' import { userListController } from './controllers/users.js' +import { questListController } from './controllers/quests.js' import { commandListController } from './controllers/commands.js' import { sessionDetailController, @@ -12,6 +13,7 @@ const router = Router() router.get('/', homeController) router.get('/users', userListController) +router.get('/quests', questListController) router.get('/commands', commandListController) router.get('/sessions', sessionListController) router.get('/sessions/:id', sessionDetailController) diff --git a/packages/analytics/tests/pagination.test.js b/packages/analytics/tests/pagination.test.js new file mode 100644 index 0000000..6d138d1 --- /dev/null +++ b/packages/analytics/tests/pagination.test.js @@ -0,0 +1,48 @@ +import mongoose from 'mongoose' +import Pagination, { ASC, DESC } from '../src/models/pagination.js' + +describe('Pagination', () => { + it('should failed when not provide order', () => { + expect(new Pagination('').getOrder()).toBe(DESC) + expect(() => new Pagination('Hahaha')).toThrow() + }) + it('should return correct limit', () => { + const pagination = new Pagination(DESC, 10) + + expect(pagination.limit()).toMatchObject({ $limit: 10 }) + }) + + it('desc should return correct order', async () => { + const pagination = new Pagination(DESC, 10) + + expect(pagination.sort('_id')).toMatchObject({ $sort: { _id: -1 } }) + expect(pagination.sort('time')).toMatchObject({ $sort: { time: -1 } }) + }) + + it('asc should return correct order', async () => { + const pagination = new Pagination(ASC, 10) + + expect(pagination.sort('_id')).toMatchObject({ $sort: { _id: 1 } }) + expect(pagination.sort('time')).toMatchObject({ $sort: { time: 1 } }) + }) + + it('should return correct match stage for empty nextKey', async () => { + const pagination = new Pagination(DESC, 10) + + expect(pagination.match('_id')).toMatchObject({ $match: {} }) + }) + + it('should return correct match stage for empty nextKey', async () => { + const nextKeyStr = '656c7fea28a4c087bfcefe12' + const pagination = new Pagination(DESC, 10, nextKeyStr) + const nextKey = new mongoose.Types.ObjectId(nextKeyStr) + + expect(pagination.match('_id')).toMatchObject({ + $match: { + _id: { + $lt: nextKey, + }, + }, + }) + }) +}) diff --git a/packages/analytics/views/commands.handlebars b/packages/analytics/views/commands.handlebars index 16f23c2..f2252cd 100644 --- a/packages/analytics/views/commands.handlebars +++ b/packages/analytics/views/commands.handlebars @@ -1,6 +1,10 @@

Commands

Error commands

+ @@ -21,4 +25,7 @@ {{/each}}
Command
+
\ No newline at end of file diff --git a/packages/analytics/views/home.handlebars b/packages/analytics/views/home.handlebars index cd8148a..d96f890 100644 --- a/packages/analytics/views/home.handlebars +++ b/packages/analytics/views/home.handlebars @@ -1,18 +1,18 @@

Welcome to {{title}}

-

Users

+

Overview

- - + + - - + + diff --git a/packages/analytics/views/layouts/main.handlebars b/packages/analytics/views/layouts/main.handlebars index e269d7b..6306555 100644 --- a/packages/analytics/views/layouts/main.handlebars +++ b/packages/analytics/views/layouts/main.handlebars @@ -8,6 +8,7 @@ diff --git a/packages/analytics/views/quests.handlebars b/packages/analytics/views/quests.handlebars new file mode 100644 index 0000000..9347e7e --- /dev/null +++ b/packages/analytics/views/quests.handlebars @@ -0,0 +1,20 @@ +

Quests

+
+
Registered Users {{userCount}}
Session Count{{sessionCount}}Total Started Quests{{totalQuests}}
Completed Session{{sessionCompleted}}Completed Quests{{totalCompleted}}
Completed Rate
+ + + + + + + + {{#each quests}} + + + + + + + + {{/each}} +
IdStartedCompletedFailedCompleted Rate
{{_id}}{{started}}{{completed}}{{failed}}{{completedRate}} %
\ No newline at end of file diff --git a/packages/analytics/views/sessionDetail.handlebars b/packages/analytics/views/sessionDetail.handlebars index d43b8fe..c209bfc 100644 --- a/packages/analytics/views/sessionDetail.handlebars +++ b/packages/analytics/views/sessionDetail.handlebars @@ -36,20 +36,20 @@ - + + - - + {{#each commands}} - + + - {{/each}} diff --git a/packages/analytics/views/sessions.handlebars b/packages/analytics/views/sessions.handlebars index 6fff971..613435f 100644 --- a/packages/analytics/views/sessions.handlebars +++ b/packages/analytics/views/sessions.handlebars @@ -1,27 +1,34 @@

Sessions

+
CommandStage PWDInput Output ErrorStageCreated AtInput Time
{{command}}{{stage}} {{pwd}}{{command}} {{output}} {{error}}{{stage}} {{createdAt}}
- + {{#each sessions}} - + {{/each}}
User Quest Status Created At Finished At Used Time Command CountSession
{{user}} {{quest}}Detail {{status}} {{createdAt}} {{finishedAt}} {{usedTime}} {{commandCount}}Detail
+
+ \ No newline at end of file diff --git a/packages/analytics/views/users.handlebars b/packages/analytics/views/users.handlebars index acc6233..dde1654 100644 --- a/packages/analytics/views/users.handlebars +++ b/packages/analytics/views/users.handlebars @@ -1,23 +1,30 @@

Users List

+
- - - - + + + + {{#each users}} - - - - - + + + + + {{/each}}
UsernameEmailRegistration MethodRegistration TimeSessions Count Last ActivitySessions CountEmailRegistration TimeRegistration Method
{{username}}{{email}}{{method}}{{createdAt}}{{sessionCount}}{{username}} {{lastActivityAt}}{{sessionCount}}{{email}}{{createdAt}}{{method}}
+
+ \ No newline at end of file diff --git a/packages/models/package.json b/packages/models/package.json index d5f58b0..74077dd 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -6,6 +6,6 @@ "license": "GPL", "exports": "./index.js", "dependencies": { - "mongoose": "^7.6.0" + "mongoose": "^8.0.3" } } diff --git a/server/package.json b/server/package.json index b127f77..9159c9b 100644 --- a/server/package.json +++ b/server/package.json @@ -26,7 +26,7 @@ "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.1", "minimist": "^1.2.8", - "mongoose": "^7.4.0", + "mongoose": "^8.0.3", "node-schedule": "^2.1.1", "passport": "^0.6.0", "passport-github2": "^0.1.12", diff --git a/yarn.lock b/yarn.lock index 17431a4..924b960 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1756,15 +1756,10 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -bson@^5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/bson/-/bson-5.4.0.tgz#0eea77276d490953ad8616b483298dbff07384c6" - integrity sha512-WRZ5SQI5GfUuKnPTNmAYPiKIof3ORXAF4IRU5UcgmivNIon01rWQlw5RUH954dpu8yGL8T59YShVddIPaU/gFA== - -bson@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/bson/-/bson-5.5.0.tgz#a419cc69f368d2def3b8b22ea03ed1c9be40e53f" - integrity sha512-B+QB4YmDx9RStKv8LLSl/aVIEV3nYJc3cJNNTK2Cd1TL+7P+cNpw9mAPeCgc5K+j01Dv6sxUzcITXDx7ZU3F0w== +bson@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/bson/-/bson-6.2.0.tgz#4b6acafc266ba18eeee111373c2699304a9ba0a3" + integrity sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q== buffer-crc32@~0.2.3: version "0.2.13" @@ -3668,11 +3663,6 @@ internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" -ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -4833,49 +4823,23 @@ mongodb-connection-string-url@^2.6.0: "@types/whatwg-url" "^8.2.1" whatwg-url "^11.0.0" -mongodb@5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.7.0.tgz#e16d2fcdfd9f8503ec2d88288392dc3235bb3ecc" - integrity sha512-zm82Bq33QbqtxDf58fLWBwTjARK3NSvKYjyz997KSy6hpat0prjeX/kxjbPVyZY60XYPDNETaHkHJI2UCzSLuw== +mongodb@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.2.0.tgz#2c9dcb3eeaf528ed850e94b3df392de6c6b0d7ab" + integrity sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA== dependencies: - bson "^5.4.0" - mongodb-connection-string-url "^2.6.0" - socks "^2.7.1" - optionalDependencies: - saslprep "^1.0.3" - -mongodb@5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.9.0.tgz#5a22065fa8cfaf1d58bf2e3c451cd2c4bfa983a2" - integrity sha512-g+GCMHN1CoRUA+wb1Agv0TI4YTSiWr42B5ulkiAfLLHitGK1R+PkSAf3Lr5rPZwi/3F04LiaZEW0Kxro9Fi2TA== - dependencies: - bson "^5.5.0" - mongodb-connection-string-url "^2.6.0" - socks "^2.7.1" - optionalDependencies: "@mongodb-js/saslprep" "^1.1.0" + bson "^6.2.0" + mongodb-connection-string-url "^2.6.0" -mongoose@^7.4.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.4.0.tgz#3726a8dea0c9e2d54651845cc84c813a6507906a" - integrity sha512-oHE1eqodfKzugXRlQxpo+msIea7jPcRoayDuEMr50+bYwM/juA5f+1stjkWlXcg6vo1PdJFVA6DGaKOPLuG5mA== +mongoose@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-8.0.3.tgz#f3708ba0f40b6dec8f5f1a5d40abd69a80207114" + integrity sha512-LJRT0yP4TW14HT4r2RkxqyvoTylMSzWpl5QOeVHTnRggCLQSpkoBdgbUtORFq/mSL2o9cLCPJz+6uzFj25qbHw== dependencies: - bson "^5.4.0" + bson "^6.2.0" kareem "2.5.1" - mongodb "5.7.0" - mpath "0.9.0" - mquery "5.0.0" - ms "2.1.3" - sift "16.0.1" - -mongoose@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.6.0.tgz#3037b754ccdf168385e52d4bcabeb7f5c3b17a5f" - integrity sha512-ztQ12rm0BQN5i7LB6xhWX4l9a9w2aa3jEwa/mM2vAutYJRyAwOzcusvKJBULMzFHyUDBOVW15grisexypgMIWA== - dependencies: - bson "^5.4.0" - kareem "2.5.1" - mongodb "5.9.0" + mongodb "6.2.0" mpath "0.9.0" mquery "5.0.0" ms "2.1.3" @@ -5758,13 +5722,6 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saslprep@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" - integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== - dependencies: - sparse-bitfield "^3.0.3" - semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -5913,11 +5870,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - socket.io-adapter@~2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" @@ -5956,14 +5908,6 @@ socket.io@^4.7.1: socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" -socks@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== - dependencies: - ip "^2.0.0" - smart-buffer "^4.2.0" - sorted-array-functions@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5"