diff --git a/.pnp.cjs b/.pnp.cjs index e789a204..eb1e0884 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -6697,6 +6697,7 @@ const RAW_RUNTIME_STATE = ["react", "npm:18.3.1"],\ ["react-dom", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:18.3.1"],\ ["react-router-dom", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:6.27.0"],\ + ["socket.io-client", "npm:4.8.1"],\ ["storybook", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:8.4.1"],\ ["tailwindcss", "npm:3.4.14"],\ ["typescript", "patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=8c6c40"],\ @@ -7491,7 +7492,21 @@ const RAW_RUNTIME_STATE = ["cors", "npm:2.8.5"],\ ["debug", "virtual:3e6e7a6bcf85063c1871dabf67ed2f9be0d915cac19f644865a637f8f5784ec89de641ce76795e5d1db4c46954238721040693f06cc5667de5817fb807db5915#npm:4.3.7"],\ ["engine.io-parser", "npm:5.2.3"],\ - ["ws", "virtual:5e1ad2a7862aee833ef56e6438cd24aa30efe06c23147978b84e2c2c2130cbe542568ae38f491d993931bedd1ed54b9b0e4d2fa2ede5d05f44e07ee33fd09dc8#npm:8.17.1"]\ + ["ws", "virtual:5eef7f59d1d1aa0582a7b75dde266b71af8bd36e062eaf8f33ffa3a9a4f4c8f703b23c6b4cc826183bdcc4769c89f98c10fa6fcbba77be1d9b4b8d97cc3e059c#npm:8.17.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["engine.io-client", [\ + ["npm:6.6.2", {\ + "packageLocation": "./.yarn/cache/engine.io-client-npm-6.6.2-5eef7f59d1-a1a0995df1.zip/node_modules/engine.io-client/",\ + "packageDependencies": [\ + ["engine.io-client", "npm:6.6.2"],\ + ["@socket.io/component-emitter", "npm:3.1.2"],\ + ["debug", "virtual:3e6e7a6bcf85063c1871dabf67ed2f9be0d915cac19f644865a637f8f5784ec89de641ce76795e5d1db4c46954238721040693f06cc5667de5817fb807db5915#npm:4.3.7"],\ + ["engine.io-parser", "npm:5.2.3"],\ + ["ws", "virtual:5eef7f59d1d1aa0582a7b75dde266b71af8bd36e062eaf8f33ffa3a9a4f4c8f703b23c6b4cc826183bdcc4769c89f98c10fa6fcbba77be1d9b4b8d97cc3e059c#npm:8.17.1"],\ + ["xmlhttprequest-ssl", "npm:2.1.2"]\ ],\ "linkType": "HARD"\ }]\ @@ -12376,7 +12391,8 @@ const RAW_RUNTIME_STATE = ["ts-jest", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:29.2.5"],\ ["ts-node", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:10.9.2"],\ ["ts-node-dev", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:2.0.0"],\ - ["typeorm", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:0.3.20"]\ + ["typeorm", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:0.3.20"],\ + ["uuid", "npm:11.0.3"]\ ],\ "linkType": "SOFT"\ }]\ @@ -12547,7 +12563,20 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["socket.io-adapter", "npm:2.5.5"],\ ["debug", "virtual:3e6e7a6bcf85063c1871dabf67ed2f9be0d915cac19f644865a637f8f5784ec89de641ce76795e5d1db4c46954238721040693f06cc5667de5817fb807db5915#npm:4.3.7"],\ - ["ws", "virtual:5e1ad2a7862aee833ef56e6438cd24aa30efe06c23147978b84e2c2c2130cbe542568ae38f491d993931bedd1ed54b9b0e4d2fa2ede5d05f44e07ee33fd09dc8#npm:8.17.1"]\ + ["ws", "virtual:5eef7f59d1d1aa0582a7b75dde266b71af8bd36e062eaf8f33ffa3a9a4f4c8f703b23c6b4cc826183bdcc4769c89f98c10fa6fcbba77be1d9b4b8d97cc3e059c#npm:8.17.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["socket.io-client", [\ + ["npm:4.8.1", {\ + "packageLocation": "./.yarn/cache/socket.io-client-npm-4.8.1-eb1c9f0ea4-544c49cc8c.zip/node_modules/socket.io-client/",\ + "packageDependencies": [\ + ["socket.io-client", "npm:4.8.1"],\ + ["@socket.io/component-emitter", "npm:3.1.2"],\ + ["debug", "virtual:3e6e7a6bcf85063c1871dabf67ed2f9be0d915cac19f644865a637f8f5784ec89de641ce76795e5d1db4c46954238721040693f06cc5667de5817fb807db5915#npm:4.3.7"],\ + ["engine.io-client", "npm:6.6.2"],\ + ["socket.io-parser", "npm:4.2.4"]\ ],\ "linkType": "HARD"\ }]\ @@ -13834,6 +13863,13 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["uuid", [\ + ["npm:11.0.3", {\ + "packageLocation": "./.yarn/cache/uuid-npm-11.0.3-abcb5b16c0-cee762fc76.zip/node_modules/uuid/",\ + "packageDependencies": [\ + ["uuid", "npm:11.0.3"]\ + ],\ + "linkType": "HARD"\ + }],\ ["npm:9.0.1", {\ "packageLocation": "./.yarn/cache/uuid-npm-9.0.1-39a8442bc6-1607dd32ac.zip/node_modules/uuid/",\ "packageDependencies": [\ @@ -14216,10 +14252,10 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ - ["virtual:5e1ad2a7862aee833ef56e6438cd24aa30efe06c23147978b84e2c2c2130cbe542568ae38f491d993931bedd1ed54b9b0e4d2fa2ede5d05f44e07ee33fd09dc8#npm:8.17.1", {\ - "packageLocation": "./.yarn/__virtual__/ws-virtual-f640a9d5b6/0/cache/ws-npm-8.17.1-f57fb24a2c-f4a49064af.zip/node_modules/ws/",\ + ["virtual:5eef7f59d1d1aa0582a7b75dde266b71af8bd36e062eaf8f33ffa3a9a4f4c8f703b23c6b4cc826183bdcc4769c89f98c10fa6fcbba77be1d9b4b8d97cc3e059c#npm:8.17.1", {\ + "packageLocation": "./.yarn/__virtual__/ws-virtual-60464f8a68/0/cache/ws-npm-8.17.1-f57fb24a2c-f4a49064af.zip/node_modules/ws/",\ "packageDependencies": [\ - ["ws", "virtual:5e1ad2a7862aee833ef56e6438cd24aa30efe06c23147978b84e2c2c2130cbe542568ae38f491d993931bedd1ed54b9b0e4d2fa2ede5d05f44e07ee33fd09dc8#npm:8.17.1"],\ + ["ws", "virtual:5eef7f59d1d1aa0582a7b75dde266b71af8bd36e062eaf8f33ffa3a9a4f4c8f703b23c6b4cc826183bdcc4769c89f98c10fa6fcbba77be1d9b4b8d97cc3e059c#npm:8.17.1"],\ ["@types/bufferutil", null],\ ["@types/utf-8-validate", null],\ ["bufferutil", null],\ @@ -14234,6 +14270,15 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["xmlhttprequest-ssl", [\ + ["npm:2.1.2", {\ + "packageLocation": "./.yarn/cache/xmlhttprequest-ssl-npm-2.1.2-7d8e3817bc-70d6086932.zip/node_modules/xmlhttprequest-ssl/",\ + "packageDependencies": [\ + ["xmlhttprequest-ssl", "npm:2.1.2"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["xtend", [\ ["npm:4.0.2", {\ "packageLocation": "./.yarn/cache/xtend-npm-4.0.2-7f2375736e-366ae4783e.zip/node_modules/xtend/",\ diff --git a/.yarn/cache/engine.io-client-npm-6.6.2-5eef7f59d1-a1a0995df1.zip b/.yarn/cache/engine.io-client-npm-6.6.2-5eef7f59d1-a1a0995df1.zip new file mode 100644 index 00000000..e5e4aad1 Binary files /dev/null and b/.yarn/cache/engine.io-client-npm-6.6.2-5eef7f59d1-a1a0995df1.zip differ diff --git a/.yarn/cache/socket.io-client-npm-4.8.1-eb1c9f0ea4-544c49cc8c.zip b/.yarn/cache/socket.io-client-npm-4.8.1-eb1c9f0ea4-544c49cc8c.zip new file mode 100644 index 00000000..6dcf5110 Binary files /dev/null and b/.yarn/cache/socket.io-client-npm-4.8.1-eb1c9f0ea4-544c49cc8c.zip differ diff --git a/.yarn/cache/uuid-npm-11.0.3-abcb5b16c0-cee762fc76.zip b/.yarn/cache/uuid-npm-11.0.3-abcb5b16c0-cee762fc76.zip new file mode 100644 index 00000000..de0763df Binary files /dev/null and b/.yarn/cache/uuid-npm-11.0.3-abcb5b16c0-cee762fc76.zip differ diff --git a/.yarn/cache/xmlhttprequest-ssl-npm-2.1.2-7d8e3817bc-70d6086932.zip b/.yarn/cache/xmlhttprequest-ssl-npm-2.1.2-7d8e3817bc-70d6086932.zip new file mode 100644 index 00000000..895b91e8 Binary files /dev/null and b/.yarn/cache/xmlhttprequest-ssl-npm-2.1.2-7d8e3817bc-70d6086932.zip differ diff --git a/packages/client/package.json b/packages/client/package.json index 3f7e1875..83faa2da 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -17,7 +17,8 @@ "qrcode.react": "^4.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.27.0" + "react-router-dom": "^6.27.0", + "socket.io-client": "^4.8.1" }, "devDependencies": { "@chromatic-com/storybook": "^3.2.2", diff --git a/packages/client/src/pages/nickname/index.tsx b/packages/client/src/pages/nickname/index.tsx index 6b5fa625..00290719 100644 --- a/packages/client/src/pages/nickname/index.tsx +++ b/packages/client/src/pages/nickname/index.tsx @@ -3,12 +3,26 @@ import AvatarIcon from '@/shared/assets/icons/avatar.svg?react'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { CustomButton } from '@/shared/ui/buttons'; +import { getCookie, setCookie } from '@/shared/utils/cookie'; +import { getQuizSocket } from '@/shared/utils/socket'; export default function Nickname() { const [nickname, setNickname] = useState(''); const navigate = useNavigate(); const handleNicknameSubmit = (nickname: string) => { + const socket = getQuizSocket(); + const sid = getCookie('sid'); + if (sid) { + socket.emit('entry', { roomId: 123456, nickname: nickname, sid: sid }); + return; + } + socket.emit('entry', { roomId: 123456, nickname: nickname }); + + socket.on('session', (response) => { + setCookie('sid', response.sid); + }); // TODO: API 연동 후 submit 함수 구현 console.log(nickname); navigate('/quiz/wait'); @@ -50,6 +64,7 @@ export default function Nickname() { }`} onClick={() => handleNicknameSubmit(nickname)} disabled={nickname.length === 0} + onClick={() => handleNicknameSubmit(nickname)} > Join diff --git a/packages/client/src/pages/quiz-wait/index.tsx b/packages/client/src/pages/quiz-wait/index.tsx index afbc1dd4..540aca70 100644 --- a/packages/client/src/pages/quiz-wait/index.tsx +++ b/packages/client/src/pages/quiz-wait/index.tsx @@ -3,6 +3,8 @@ import { generateRandomPositions } from '@/shared/utils/generateRandomPositions' import { QRCodeSVG } from 'qrcode.react'; import { useLayoutEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { getQuizSocket } from '@/shared/utils/socket'; +import { getCookie } from '@/shared/utils/cookie'; // TODO: 파일 분리 const GUEST_DISPLAY_SIZE = { width: 1020, height: 576 }; @@ -11,16 +13,21 @@ const BUTTON_SIZE = { width: 74, height: 44 }; // TODO: API 연동 후 삭제 const fakeLink = 'https://google.com'; -const fakeGuests = ['도훈', '성현', '병찬', '채원']; const from = { x: SPACING, y: SPACING }; const to = { x: GUEST_DISPLAY_SIZE.width - SPACING, y: GUEST_DISPLAY_SIZE.height - SPACING }; -const count = fakeGuests.length; export default function QuizWait() { const buttonRefs = useRef([]); const [buttonSize, setButtonSize] = useState(BUTTON_SIZE); const navigate = useNavigate(); + const [guests, setGuests] = useState([]); + const guestCount = guests.length; + const socket = getQuizSocket(); + + socket.on('nickname', (response) => { + setGuests((prev) => [response.nickname, ...prev]); + }); useLayoutEffect(() => { if (buttonRefs.current.length > 0) { @@ -34,7 +41,7 @@ export default function QuizWait() { } }, []); - const randomPositions = generateRandomPositions({ from, to, count, buttonSize }); + const randomPositions = generateRandomPositions({ from, to, count: guestCount, buttonSize }); const handleCopyLink = () => { try { @@ -47,9 +54,7 @@ export default function QuizWait() { }; const handleQuizStart = () => { - // DEMO 용도로 바로 퀴즈 페이지로 이동 추후 삭제 - navigate('/quiz/session'); - // TODO: socket.emit을 통해 전체 사용자에게 퀴즈 시작 이벤트 전달 + socket.emit('master entry', { classId: '123', sid: getCookie('sid') }); }; return ( @@ -79,7 +84,7 @@ export default function QuizWait() { } }} > - + ))} diff --git a/packages/client/src/shared/utils/cookie.ts b/packages/client/src/shared/utils/cookie.ts new file mode 100644 index 00000000..c2f205b5 --- /dev/null +++ b/packages/client/src/shared/utils/cookie.ts @@ -0,0 +1,21 @@ +export function getCookie(name: string) { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + + if (parts.length === 2) { + return parts.pop()?.split(';').shift(); + } + return undefined; +} + +export function setCookie(name: string, val: any, day?: number) { + const date = new Date(); + const value = val; + // day가 없는 경우 세션쿠키로 설정 + if (day) { + date.setTime(date.getTime() + day * 24 * 60 * 60 * 1000); + document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`; + } else { + document.cookie = `${name}=${value}; path=/`; + } +} diff --git a/packages/client/src/shared/utils/socket.ts b/packages/client/src/shared/utils/socket.ts new file mode 100644 index 00000000..0176a6d6 --- /dev/null +++ b/packages/client/src/shared/utils/socket.ts @@ -0,0 +1,10 @@ +import { io, Socket } from 'socket.io-client'; + +let socket: Socket | null = null; + +export function getQuizSocket(): Socket { + if (!socket) { + socket = io('http://localhost:3000/game'); + } + return socket; +} diff --git a/packages/server/package.json b/packages/server/package.json index 0161e8e4..689096e7 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -30,7 +30,8 @@ "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "socket.io": "^4.8.1", - "typeorm": "^0.3.20" + "typeorm": "^0.3.20", + "uuid": "^11.0.3" }, "devDependencies": { "@nestjs/testing": "^10.4.6", diff --git a/packages/server/src/app.module.ts b/packages/server/src/app.module.ts index 8407b840..1fc840bd 100644 --- a/packages/server/src/app.module.ts +++ b/packages/server/src/app.module.ts @@ -10,6 +10,7 @@ import { QuizModule } from './module/quiz/quiz.module'; import { GameGateway } from './module/game/game.gateway'; import { RedisService } from './config/database/redis/redis.service'; import { RedisModule } from '@nestjs-modules/ioredis'; // 추가 +import { GameService } from './module/game/games/game.service'; @Module({ imports: [ UserModule, @@ -36,7 +37,7 @@ import { RedisModule } from '@nestjs-modules/ioredis'; // 추가 }), ], controllers: [AppController], - providers: [AppService, GameGateway, RedisService], + providers: [AppService, GameGateway, RedisService, GameService], exports: [RedisService], }) export class AppModule {} diff --git a/packages/server/src/module/game/game.gateway.ts b/packages/server/src/module/game/game.gateway.ts index a874245b..566f4c49 100644 --- a/packages/server/src/module/game/game.gateway.ts +++ b/packages/server/src/module/game/game.gateway.ts @@ -6,6 +6,9 @@ import { OnGatewayDisconnect, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; +import { RedisService } from '../../config/database/redis/redis.service'; +import { v4 as uuidv4 } from 'uuid'; +import { GameService } from './games/game.service'; @WebSocketGateway({ cors: { @@ -17,7 +20,10 @@ export class GameGateway implements OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() server: Server; - constructor() {} + constructor( + private readonly redisService: RedisService, + private readonly gameService: GameService, + ) {} // 클라이언트가 연결했을 때 처리하는 메서드 async handleConnection(client: Socket) { console.log(`Client connected: ${client.id}`); @@ -28,10 +34,58 @@ export class GameGateway implements OnGatewayConnection, OnGatewayDisconnect { console.log(`Client disconnected: ${client.id}`); } - // 클라이언트로부터 메시지를 받을 때 처리하는 메서드 - @SubscribeMessage('message') - handleMessage(client: Socket, payload: any): string { - console.log('Received message:', payload, client.id); - return 'server의 응답'; + @SubscribeMessage('Quiz') + handleQuiz(client: Socket, payload: any) { + // classID, 퀴즈의 정보를 가져온다. + const quiz = this.redisService.get(payload.classId); + } + + @SubscribeMessage('submit') + handleSubmit() {} + + @SubscribeMessage('master entry') + async handleMasterEntry(client: Socket, payload: any) { + // 방장이 게임을 나가도 재접속이 가능하며, 게임은 지속된다. + + const masterSid = uuidv4(); + const pinCode = uuidv4().slice(0, 6); // 메소드 분리해서 중복 확인하고 없을 때까지 반복 + + client.join(pinCode); // pinCode로 되어 있는 roomd을 들어감 + + this.redisService.set(`master_sid=${masterSid}`, pinCode); + + const { classId } = payload; + const gameInfo = { classId, currentOrder: 0, participantList: [] }; + + this.redisService.set(`gameId=${pinCode}`, JSON.stringify(gameInfo)); + + // refactor: 캐싱이 되어있다면 기간 연장, 안되어있다면 MySQL에서 데이터 가져오기, 데이터 전처리 + const quizData = await this.gameService.cachingQuizData(classId); + this.redisService.set(`classId=${classId}`, JSON.stringify(quizData)); + + client.emit('session', masterSid); + client.emit('pincode', pinCode); + } + + @SubscribeMessage('participant entry') + async handleParticipantEntry(client: Socket, payload: any) { + const { pinCode, nickname } = payload; + const clientInfo = { pinCode, nickname }; + + client.join(pinCode); // 같은 룸에 들어오게 된것 + + const participantSid = uuidv4(); + this.redisService.set(`participant_sid=${participantSid}`, JSON.stringify(clientInfo)); + client.emit('session', participantSid); + + // 레디스 갱신 + const gameInfo = JSON.parse(await this.redisService.get(`gameId=${pinCode}`)); + const newParticipantList = gameInfo.participantList.push(nickname); + this.redisService.set(`gameId=${pinCode}`, JSON.stringify(gameInfo)); + + client.emit('nickname', newParticipantList); + client.to(pinCode).emit('nickname', newParticipantList); + + return; } } diff --git a/packages/server/src/module/game/games/game.service.ts b/packages/server/src/module/game/games/game.service.ts new file mode 100644 index 00000000..9c6e74a4 --- /dev/null +++ b/packages/server/src/module/game/games/game.service.ts @@ -0,0 +1,19 @@ +import { Injectable, HttpException, HttpStatus, Param } from '@nestjs/common'; +import { ChoiceRepository } from '../../quiz/quizzes/repositories/choice.repository'; +import { ClassRepository } from '../../quiz/quizzes/repositories/class.repository'; +import { QuizRepository } from '../../quiz/quizzes/repositories/quiz.repository'; + +@Injectable() +export class GameService { + constructor( + private readonly classRepository: ClassRepository, + private readonly quizRepository: QuizRepository, + private readonly choiceRepository: ChoiceRepository, + ) {} + + async cachingQuizData(classId: number) { + const classWithRelations = await this.classRepository.findClassWithRelations(classId); + + return classWithRelations; + } +} diff --git a/yarn.lock b/yarn.lock index 2a39e85f..c185984f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4450,6 +4450,7 @@ __metadata: react: "npm:^18.3.1" react-dom: "npm:^18.3.1" react-router-dom: "npm:^6.27.0" + socket.io-client: "npm:^4.8.1" storybook: "npm:^8.4.1" tailwindcss: "npm:^3.4.14" typescript: "npm:^5.6.3" @@ -5075,6 +5076,19 @@ __metadata: languageName: node linkType: hard +"engine.io-client@npm:~6.6.1": + version: 6.6.2 + resolution: "engine.io-client@npm:6.6.2" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.3.1" + engine.io-parser: "npm:~5.2.1" + ws: "npm:~8.17.1" + xmlhttprequest-ssl: "npm:~2.1.1" + checksum: 10c0/a1a0995df1ce2425b43c7dd396cf4ef12d3ca85973b63e1b7bd3933d0292459e922d6be25d14013c9608dc7159ae1e10cd7005754b02bc42d40450381f691859 + languageName: node + linkType: hard + "engine.io-parser@npm:~5.2.1": version: 5.2.3 resolution: "engine.io-parser@npm:5.2.3" @@ -9364,6 +9378,7 @@ __metadata: ts-node: "npm:^10.9.2" ts-node-dev: "npm:^2.0.0" typeorm: "npm:^0.3.20" + uuid: "npm:^11.0.3" languageName: unknown linkType: soft @@ -9490,6 +9505,18 @@ __metadata: languageName: node linkType: hard +"socket.io-client@npm:^4.8.1": + version: 4.8.1 + resolution: "socket.io-client@npm:4.8.1" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.3.2" + engine.io-client: "npm:~6.6.1" + socket.io-parser: "npm:~4.2.4" + checksum: 10c0/544c49cc8cc77118ef68b758a8a580c8e680a5909cae05c566d2cc07ec6cd6720a4f5b7e985489bf2a8391749177a5437ac30b8afbdf30b9da6402687ad51c86 + languageName: node + linkType: hard + "socket.io-parser@npm:~4.2.4": version: 4.2.4 resolution: "socket.io-parser@npm:4.2.4" @@ -10577,6 +10604,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^11.0.3": + version: 11.0.3 + resolution: "uuid@npm:11.0.3" + bin: + uuid: dist/esm/bin/uuid + checksum: 10c0/cee762fc76d949a2ff9205770334699e0043d52bb766472593a25f150077c9deed821230251ea3d6ab3943a5ea137d2826678797f1d5f6754c7ce5ce27e9f7a6 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -10898,6 +10934,13 @@ __metadata: languageName: node linkType: hard +"xmlhttprequest-ssl@npm:~2.1.1": + version: 2.1.2 + resolution: "xmlhttprequest-ssl@npm:2.1.2" + checksum: 10c0/70d60869323e823f473a238f78fd108437edbc3690ecd5859c39c83217080090a18899b272e515769c0d1f518cc64cbed6b6995b23fdd7ba13b297d530b6e631 + languageName: node + linkType: hard + "xtend@npm:^4.0.0": version: 4.0.2 resolution: "xtend@npm:4.0.2"