commit a28bc286a15c30137bb7a5791f780f6dcbbe8540 Author: josedario87 Date: Wed Aug 6 02:32:18 2025 -0600 feat: implement competitive clicker MVP with Colyseus.js - Add real-time multiplayer game server with Colyseus - Implement unique player naming system with auto-increment - Create lobby system with automatic matchmaking - Build 10-minute competitive clicking game rooms (max 2 players) - Add admin dashboard for game management (pause/resume/restart/kick) - Implement Vue 3 client with professional UI - Add WebSocket communication with state synchronization - Include TypeScript throughout with proper typing - Create REST API for admin operations - Add reconnection support and error handling diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bfdff7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# Dependencies +node_modules/ +*/node_modules/ + +# Build outputs +dist/ +build/ +*/dist/ +*/build/ + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Cache directories +.cache/ +.parcel-cache/ +*.tsbuildinfo + +# Editor directories and files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Temporary files +*.tmp +*.temp \ No newline at end of file diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..0f3ea15 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,85 @@ +# Quick Start Guide + +## 🚀 Running the Project + +The project is now ready to run! Both server and client are configured and running. + +### Current Status: +- ✅ **Server**: Running on http://localhost:3000 +- ✅ **Client**: Running on http://localhost:5173 +- ✅ **WebSocket**: ws://localhost:3000 +- ✅ **Admin Monitor**: http://localhost:3000/colyseus +- ✅ **Admin Dashboard**: http://localhost:5173/dashboard + +## 📝 How to Use + +### For Players: +1. Open http://localhost:5173 in your browser +2. Enter your desired username +3. Click "Quick Play" to join a game +4. Click as fast as you can when the game starts! +5. Games last 10 minutes, winner is the one with most clicks + +### For Admins: +1. Open http://localhost:5173/dashboard to access the admin panel +2. Monitor all active games and players +3. Control games: pause, resume, restart, or kick players +4. View real-time statistics + +## 🛠️ Development Commands + +If you need to restart the services: + +```bash +# Kill existing processes +pkill -f "ts-node" +pkill -f "vite" + +# Start server (from server directory) +cd server +npx ts-node src/index.ts + +# Start client (from client directory - in another terminal) +cd client +npm run dev +``` + +## 🎮 Game Flow + +1. **Lobby**: Players join and set their names +2. **Matchmaking**: Automatic pairing when 2 players are ready +3. **Battle**: 10-minute clicking competition +4. **Results**: Winner announced, new game starts automatically + +## 📊 Monitoring + +- **Colyseus Monitor**: http://localhost:3000/colyseus - Built-in room monitoring +- **Admin Dashboard**: http://localhost:5173/dashboard - Custom admin interface +- **API Health**: http://localhost:3000/health - Server health check + +## 🔍 Troubleshooting + +If you encounter issues: + +1. Check server is running: `curl http://localhost:3000/health` +2. Check client is accessible: Open http://localhost:5173 +3. Check console for WebSocket connection errors +4. Ensure ports 3000 and 5173 are not in use by other applications + +## 🎯 Next Steps + +The MVP is complete with: +- ✅ Real-time multiplayer gameplay +- ✅ Automatic matchmaking +- ✅ Unique player naming system +- ✅ Admin controls +- ✅ Professional UI with animations +- ✅ WebSocket communication +- ✅ TypeScript throughout + +You can now: +- Test the game with multiple browser tabs +- Customize the UI styles +- Add sound effects +- Implement additional game modes +- Deploy to production \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..381ce08 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Snatch Game - Competitive Clicker + +A real-time multiplayer competitive clicker game built with Colyseus.js and Vue 3. + +## Features + +- 🎮 Real-time multiplayer gameplay +- 🏆 10-minute competitive clicking battles +- 👥 2 players per room with automatic matchmaking +- 🎯 Unique player naming system with auto-increment +- 🎛️ Admin dashboard for game management +- 📊 Real-time statistics and monitoring +- 🔄 Automatic reconnection support +- ⏸️ Pause/Resume/Restart game controls + +## Tech Stack + +- **Backend**: Colyseus.js, Express, TypeScript +- **Frontend**: Vue 3, Vite, TypeScript +- **Real-time**: WebSockets +- **Styling**: Custom CSS + +## Installation + +```bash +# Install all dependencies +npm run install:all +``` + +## Development + +```bash +# Start both server and client in development mode +npm run dev +``` + +This will start: +- Server on http://localhost:3000 +- Client on http://localhost:5173 + +## URLs + +- **Game**: http://localhost:5173 +- **Admin Dashboard**: http://localhost:5173/dashboard +- **Colyseus Monitor**: http://localhost:3000/colyseus +- **API Endpoints**: http://localhost:3000/api + +## Project Structure + +``` +snatchgame/ +├── server/ # Backend Colyseus server +│ ├── src/ +│ │ ├── rooms/ # Game and Lobby rooms +│ │ ├── schemas/ # State schemas +│ │ ├── utils/ # Utilities (name manager) +│ │ ├── adminApi.ts # Admin REST API +│ │ └── index.ts # Server entry point +│ └── package.json +│ +├── client/ # Frontend Vue application +│ ├── src/ +│ │ ├── views/ # Vue components (Lobby, Game, Dashboard) +│ │ ├── services/ # Colyseus client service +│ │ ├── router/ # Vue Router +│ │ └── main.ts # App entry point +│ └── package.json +│ +└── shared/ # Shared types between client and server + └── types.ts +``` + +## Game Rules + +1. Players enter the lobby and choose a username +2. Click "Quick Play" to join or create a game room +3. Game starts when 2 players join a room +4. Players have 10 minutes to click as fast as possible +5. The player with the most clicks wins +6. Games automatically restart after finishing + +## Admin Dashboard + +The admin dashboard provides: +- View all active rooms and their status +- Pause/Resume games in progress +- Restart games +- Kick players from rooms +- View real-time statistics +- Monitor server performance + +## API Endpoints + +- `GET /api/rooms` - List all active rooms +- `GET /api/rooms/:roomId/stats` - Get room statistics +- `POST /api/rooms/:roomId/pause` - Pause a game +- `POST /api/rooms/:roomId/resume` - Resume a game +- `POST /api/rooms/:roomId/restart` - Restart a game +- `POST /api/rooms/:roomId/kick/:playerId` - Kick a player +- `GET /api/stats` - Get global server statistics \ No newline at end of file diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..9651358 --- /dev/null +++ b/client/index.html @@ -0,0 +1,13 @@ + + + + + + + Snatch Game - Competitive Clicker + + +
+ + + \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..5eb777b --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,1641 @@ +{ + "name": "snatchgame-client", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "snatchgame-client", + "version": "1.0.0", + "dependencies": { + "colyseus.js": "latest", + "vue": "latest", + "vue-router": "latest" + }, + "devDependencies": { + "@vitejs/plugin-vue": "latest", + "@vue/tsconfig": "latest", + "typescript": "latest", + "vite": "latest", + "vue-tsc": "latest" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colyseus/httpie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@colyseus/httpie/-/httpie-2.0.1.tgz", + "integrity": "sha512-JvABMZzPLiyrUsVj3ElXGORRDTu+NKzXHWd1uV1R1SThAKMm06cVW6bOyADARD65bs8JJoHNNbUkW8KoRvRDzA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@colyseus/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@colyseus/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-MuwPFhizFKC3zmGfy0fpo+kcnZdNdnQHFVjw81v4WXHCelDeCX8yNRVtuEm8kGlHqq7qiASLC0pu0RPqYOhxXg==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/@colyseus/schema": { + "version": "3.0.50", + "resolved": "https://registry.npmjs.org/@colyseus/schema/-/schema-3.0.50.tgz", + "integrity": "sha512-r9OPVj0VX/7bA63Bta1VTWRsL4f3UrU5u+aldC2HT3HfTUlDk86ZCbrNXIH7oxH6f5pgeoLkbYy8maJNMdf5oQ==", + "license": "MIT", + "bin": { + "schema-codegen": "bin/schema-codegen", + "schema-debug": "bin/schema-debug" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.22", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.22.tgz", + "integrity": "sha512-gp4M7Di5KgNyIyO903wTClYBavRt6UyFNpc5LWfyZr1lBsTUY+QrVZfmbNF2aCyfklBOVk9YC4p+zkwoyT7ECg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.22" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.22", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.22.tgz", + "integrity": "sha512-L2nVr/1vei0xKRgO2tYVXtJYd09HTRjaZi418e85Q+QdbbqA8h7bBjfNyPPSsjnrOO4l4kaAo78c8SQUAdHvgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.22", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.22.tgz", + "integrity": "sha512-6ZczlJW1/GWTrNnkmZxJp4qyBt/SGVlcTuCWpI5zLrdPdCZsj66Aff9ZsfFaT3TyjG8zVYgBMYPuCm/eRkpcpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.22", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.5.tgz", + "integrity": "sha512-gCEjn9Ik7I/seHVNIEipOm8W+f3/kg60e8s1IgIkMYma2wu9ZGUTMv3mSL2bX+Md2L8fslceJ4SU8j1fgSRoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.22", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^2.0.5", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "vue": "3.5.18" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz", + "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/alien-signals": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.6.tgz", + "integrity": "sha512-P3TxJSe31bUHBiblg59oU1PpaWPtmxF9GhJ/cB7OkgJ0qN/ifFSKUI25/v8ZhsT+lIG6ac8DpTOplXxORX6F3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/colyseus.js": { + "version": "0.16.19", + "resolved": "https://registry.npmjs.org/colyseus.js/-/colyseus.js-0.16.19.tgz", + "integrity": "sha512-48cVPt5+21R79itIHpcJplMFt7HEks5chxPXtM9l+AvmFjfXO8UykIV4j+fYyKaJu6EB14AbBH0AnTvpQqnAEg==", + "license": "MIT", + "dependencies": { + "@colyseus/httpie": "^2.0.0", + "@colyseus/msgpackr": "^1.11.2", + "@colyseus/schema": "^3.0.0", + "tslib": "^2.1.0", + "ws": "^8.13.0" + }, + "engines": { + "node": ">= 12.x" + }, + "funding": { + "url": "https://github.com/sponsors/endel" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.5.tgz", + "integrity": "sha512-PsTFN9lo1HJCrZw9NoqjYcAbYDXY0cOKyuW2E7naX5jcaVyWpqEsZOHN9Dws5890E8e5SDAD4L4Zam3dxG3/Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.22", + "@vue/language-core": "3.0.5" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..d8bb7a2 --- /dev/null +++ b/client/package.json @@ -0,0 +1,23 @@ +{ + "name": "snatchgame-client", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "latest", + "vue-router": "latest", + "colyseus.js": "latest" + }, + "devDependencies": { + "@vitejs/plugin-vue": "latest", + "@vue/tsconfig": "latest", + "typescript": "latest", + "vite": "latest", + "vue-tsc": "latest" + } +} \ No newline at end of file diff --git a/client/src/App.vue b/client/src/App.vue new file mode 100644 index 0000000..b07292f --- /dev/null +++ b/client/src/App.vue @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/client/src/main.ts b/client/src/main.ts new file mode 100644 index 0000000..22613b8 --- /dev/null +++ b/client/src/main.ts @@ -0,0 +1,9 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import router from './router'; + +const app = createApp(App); + +app.use(router); + +app.mount('#app'); \ No newline at end of file diff --git a/client/src/router/index.ts b/client/src/router/index.ts new file mode 100644 index 0000000..2616cff --- /dev/null +++ b/client/src/router/index.ts @@ -0,0 +1,27 @@ +import { createRouter, createWebHistory } from 'vue-router'; +import Lobby from '../views/Lobby.vue'; +import Game from '../views/Game.vue'; +import Dashboard from '../views/Dashboard.vue'; + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path: '/', + name: 'Lobby', + component: Lobby + }, + { + path: '/game', + name: 'Game', + component: Game + }, + { + path: '/dashboard', + name: 'Dashboard', + component: Dashboard + } + ] +}); + +export default router; \ No newline at end of file diff --git a/client/src/services/colyseus.ts b/client/src/services/colyseus.ts new file mode 100644 index 0000000..ba78570 --- /dev/null +++ b/client/src/services/colyseus.ts @@ -0,0 +1,193 @@ +import { Client, Room } from "colyseus.js"; +import { ref, Ref } from "vue"; + +export interface PlayerData { + sessionId: string; + name: string; + clicks: number; + connected?: boolean; +} + +export interface LobbyPlayer { + sessionId: string; + name: string; + inGame: boolean; +} + +export interface AvailableRoom { + roomId: string; + playerCount: number; + status: string; +} + +class ColyseusService { + private client: Client; + private currentRoom: Room | null = null; + + public lobbyRoom: Ref = ref(null); + public gameRoom: Ref = ref(null); + public playerName: Ref = ref(""); + public sessionId: Ref = ref(""); + + constructor() { + this.client = new Client("ws://localhost:3000"); + } + + async joinLobby(): Promise { + try { + const room = await this.client.joinOrCreate("lobby"); + this.lobbyRoom.value = room; + this.currentRoom = room; + + room.onMessage("welcome", (data) => { + this.sessionId.value = data.sessionId; + this.playerName.value = data.assignedName; + }); + + room.onMessage("nameUpdated", (data) => { + this.playerName.value = data.name; + }); + + return room; + } catch (error) { + console.error("Failed to join lobby:", error); + throw error; + } + } + + async setPlayerName(name: string): Promise { + if (this.lobbyRoom.value) { + this.lobbyRoom.value.send("setName", name); + } + } + + async quickPlay(): Promise { + if (this.lobbyRoom.value) { + return new Promise((resolve, reject) => { + const room = this.lobbyRoom.value!; + + room.onMessage("roomReservation", async (reservation) => { + try { + await this.joinGameByReservation(reservation); + resolve(); + } catch (error) { + reject(error); + } + }); + + room.onMessage("error", (error) => { + reject(new Error(error.message)); + }); + + room.send("quickPlay"); + }); + } + } + + async joinGameRoom(roomId: string): Promise { + if (this.lobbyRoom.value) { + return new Promise((resolve, reject) => { + const room = this.lobbyRoom.value!; + + room.onMessage("roomReservation", async (reservation) => { + try { + await this.joinGameByReservation(reservation); + resolve(); + } catch (error) { + reject(error); + } + }); + + room.onMessage("error", (error) => { + reject(new Error(error.message)); + }); + + room.send("joinRoom", roomId); + }); + } + } + + private async joinGameByReservation(reservation: any): Promise { + try { + const room = await this.client.consumeSeatReservation(reservation); + this.gameRoom.value = room; + this.currentRoom = room; + + room.onMessage("playerInfo", (data) => { + this.sessionId.value = data.sessionId; + this.playerName.value = data.name; + }); + + if (this.lobbyRoom.value) { + this.lobbyRoom.value.leave(); + this.lobbyRoom.value = null; + } + } catch (error) { + console.error("Failed to join game room:", error); + throw error; + } + } + + sendClick(): void { + if (this.gameRoom.value) { + this.gameRoom.value.send("click"); + } + } + + leaveCurrentRoom(): void { + if (this.currentRoom) { + this.currentRoom.leave(); + this.currentRoom = null; + this.gameRoom.value = null; + this.lobbyRoom.value = null; + } + } + + async fetchRooms(): Promise { + try { + const response = await fetch("http://localhost:3000/api/rooms"); + return await response.json(); + } catch (error) { + console.error("Failed to fetch rooms:", error); + return []; + } + } + + async fetchRoomStats(roomId: string): Promise { + try { + const response = await fetch(`http://localhost:3000/api/rooms/${roomId}/stats`); + return await response.json(); + } catch (error) { + console.error("Failed to fetch room stats:", error); + return null; + } + } + + async pauseRoom(roomId: string): Promise { + await fetch(`http://localhost:3000/api/rooms/${roomId}/pause`, { method: "POST" }); + } + + async resumeRoom(roomId: string): Promise { + await fetch(`http://localhost:3000/api/rooms/${roomId}/resume`, { method: "POST" }); + } + + async restartRoom(roomId: string): Promise { + await fetch(`http://localhost:3000/api/rooms/${roomId}/restart`, { method: "POST" }); + } + + async kickPlayer(roomId: string, playerId: string): Promise { + await fetch(`http://localhost:3000/api/rooms/${roomId}/kick/${playerId}`, { method: "POST" }); + } + + async fetchGlobalStats(): Promise { + try { + const response = await fetch("http://localhost:3000/api/stats"); + return await response.json(); + } catch (error) { + console.error("Failed to fetch global stats:", error); + return null; + } + } +} + +export const colyseusService = new ColyseusService(); \ No newline at end of file diff --git a/client/src/views/Dashboard.vue b/client/src/views/Dashboard.vue new file mode 100644 index 0000000..e2faadc --- /dev/null +++ b/client/src/views/Dashboard.vue @@ -0,0 +1,540 @@ + + + + + \ No newline at end of file diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue new file mode 100644 index 0000000..c0bfebe --- /dev/null +++ b/client/src/views/Game.vue @@ -0,0 +1,494 @@ + + + + + \ No newline at end of file diff --git a/client/src/views/Lobby.vue b/client/src/views/Lobby.vue new file mode 100644 index 0000000..df0e15b --- /dev/null +++ b/client/src/views/Lobby.vue @@ -0,0 +1,405 @@ + + + + + \ No newline at end of file diff --git a/client/src/vite-env.d.ts b/client/src/vite-env.d.ts new file mode 100644 index 0000000..151aa68 --- /dev/null +++ b/client/src/vite-env.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..8e31136 --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "preserve", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} \ No newline at end of file diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json new file mode 100644 index 0000000..ccf3d8f --- /dev/null +++ b/client/tsconfig.node.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} \ No newline at end of file diff --git a/client/vite.config.ts b/client/vite.config.ts new file mode 100644 index 0000000..e152b13 --- /dev/null +++ b/client/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + port: 5173, + proxy: { + '/api': { + target: 'http://localhost:3000', + changeOrigin: true + } + } + } +}) \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..36b3b91 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "snatchgame", + "version": "1.0.0", + "description": "Competitive clicker game with Colyseus.js", + "scripts": { + "install:all": "npm run install:server && npm run install:client", + "install:server": "cd server && npm install", + "install:client": "cd client && npm install", + "dev": "npm run dev:server & npm run dev:client", + "dev:server": "cd server && npm run dev", + "dev:client": "cd client && npm run dev", + "build": "npm run build:server && npm run build:client", + "build:server": "cd server && npm run build", + "build:client": "cd client && npm run build" + }, + "author": "", + "license": "MIT" +} \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json new file mode 100644 index 0000000..2c361ef --- /dev/null +++ b/server/package-lock.json @@ -0,0 +1,2557 @@ +{ + "name": "snatchgame-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "snatchgame-server", + "version": "1.0.0", + "dependencies": { + "@colyseus/monitor": "latest", + "@colyseus/schema": "latest", + "colyseus": "latest", + "cors": "latest", + "express": "latest" + }, + "devDependencies": { + "@types/cors": "latest", + "@types/express": "latest", + "@types/node": "latest", + "ts-node-dev": "^2.0.0", + "typescript": "latest" + } + }, + "node_modules/@colyseus/auth": { + "version": "0.16.6", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/jsonwebtoken": "^9.0.5", + "connect-redis": "^7.1.0", + "express-jwt": "^8.5.1", + "express-session": "^1.17.3", + "grant": "^5.4.24", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 18.x" + }, + "funding": { + "url": "https://github.com/sponsors/endel" + }, + "peerDependencies": { + "@colyseus/core": "0.16.x", + "express": ">=4.16.0" + } + }, + "node_modules/@colyseus/clock": { + "version": "1.0.0", + "license": "MIT", + "peer": true + }, + "node_modules/@colyseus/core": { + "version": "0.16.19", + "license": "MIT", + "peer": true, + "dependencies": { + "@colyseus/greeting-banner": "^2.0.6", + "@colyseus/msgpackr": "^1.11.2", + "@colyseus/timer": "^1.0.1", + "debug": "^4.3.4", + "nanoid": "^2.0.0" + }, + "engines": { + "node": ">= 18.x" + }, + "funding": { + "url": "https://github.com/sponsors/endel" + }, + "peerDependencies": { + "@colyseus/schema": "^3.0.0" + } + }, + "node_modules/@colyseus/greeting-banner": { + "version": "2.0.6", + "license": "MIT", + "peer": true + }, + "node_modules/@colyseus/monitor": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@colyseus/monitor/-/monitor-0.16.7.tgz", + "integrity": "sha512-kkSsEmD38/Rcgy/599Kw1QWfxIwiAdGPHQRfzZF0nsUxWLTNHGfypeEJUeajrnoyYxJFZdKwIma0SSzxmLDCig==", + "license": "MIT", + "dependencies": { + "express": ">=4.16.0", + "node-os-utils": "^1.2.0" + } + }, + "node_modules/@colyseus/msgpackr": { + "version": "1.11.2", + "license": "MIT", + "peer": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/@colyseus/redis-driver": { + "version": "0.16.1", + "license": "MIT", + "peer": true, + "dependencies": { + "@colyseus/core": "^0.16.4", + "ioredis": "^5.3.2" + } + }, + "node_modules/@colyseus/redis-presence": { + "version": "0.16.4", + "license": "MIT", + "peer": true, + "dependencies": { + "@colyseus/core": "^0.16.19", + "ioredis": "^5.3.2" + } + }, + "node_modules/@colyseus/schema": { + "version": "3.0.50", + "resolved": "https://registry.npmjs.org/@colyseus/schema/-/schema-3.0.50.tgz", + "integrity": "sha512-r9OPVj0VX/7bA63Bta1VTWRsL4f3UrU5u+aldC2HT3HfTUlDk86ZCbrNXIH7oxH6f5pgeoLkbYy8maJNMdf5oQ==", + "license": "MIT", + "bin": { + "schema-codegen": "bin/schema-codegen", + "schema-debug": "bin/schema-debug" + } + }, + "node_modules/@colyseus/timer": { + "version": "1.0.1", + "license": "MIT", + "peer": true, + "dependencies": { + "@colyseus/clock": "^1.0.0" + } + }, + "node_modules/@colyseus/uwebsockets-transport": { + "version": "0.16.9", + "license": "MIT", + "peer": true, + "dependencies": { + "uwebsockets-express": "^1.3.8", + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.49.0" + }, + "peerDependencies": { + "@colyseus/core": "0.16.x" + } + }, + "node_modules/@colyseus/ws-transport": { + "version": "0.16.5", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/ws": "^8.5.14", + "ws": "^8.18.0" + }, + "peerDependencies": { + "@colyseus/core": "0.16.x" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ioredis/commands": { + "version": "1.3.0", + "license": "MIT", + "peer": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", + "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT", + "peer": true + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bn.js": { + "version": "4.12.2", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/body-parser": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/colyseus": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/colyseus/-/colyseus-0.16.4.tgz", + "integrity": "sha512-2SSfWj/onUD5l1rtayApOTFoKgk7/3T5Br+eOazpA9nk08G6IuUIeHXy1KJcruRwUf+ROndddiUC5qbSIPZySQ==", + "license": "MIT", + "engines": { + "node": ">= 20.x" + }, + "peerDependencies": { + "@colyseus/auth": "0.16.x", + "@colyseus/core": "0.16.x", + "@colyseus/redis-driver": "0.16.x", + "@colyseus/redis-presence": "0.16.x", + "@colyseus/schema": "^3.0.0", + "@colyseus/uwebsockets-transport": "0.16.x", + "@colyseus/ws-transport": "0.16.x" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/connect-redis": { + "version": "7.1.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "express-session": ">=1" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/denque": { + "version": "2.1.0", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/elliptic": { + "version": "6.6.1", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-jwt": { + "version": "8.5.1", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/express-session": { + "version": "1.18.2", + "license": "MIT", + "peer": true, + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "license": "MIT", + "peer": true + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "license": "MIT", + "peer": true + }, + "node_modules/express-unless": { + "version": "2.1.3", + "license": "MIT", + "peer": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/grant": { + "version": "5.4.24", + "license": "MIT", + "peer": true, + "dependencies": { + "qs": "^6.14.0", + "request-compose": "^2.1.7", + "request-oauth": "^1.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "optionalDependencies": { + "cookie": "^0.7.2", + "cookie-signature": "^1.2.2", + "jwk-to-pem": "^2.0.7", + "jws": "^4.0.0" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "license": "MIT", + "peer": true + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ioredis": { + "version": "5.7.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@ioredis/commands": "^1.3.0", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "license": "MIT", + "peer": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.2", + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "license": "MIT", + "peer": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwk-to-pem": { + "version": "2.0.7", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.6.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "license": "MIT", + "peer": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "license": "MIT", + "peer": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "license": "ISC", + "optional": true, + "peer": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/msgpackr-extract/node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/msgpackr-extract/node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/msgpackr-extract/node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/msgpackr-extract/node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/msgpackr-extract/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/nanoid": { + "version": "2.1.11", + "license": "MIT", + "peer": true + }, + "node_modules/negotiator": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-os-utils": { + "version": "1.3.7", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "license": "MIT", + "peer": true, + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/request-compose": { + "version": "2.1.7", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/request-oauth": { + "version": "1.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "oauth-sign": "^0.9.0", + "qs": "^6.9.6", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/router": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "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", + "license": "MIT", + "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", + "license": "MIT", + "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", + "license": "MIT", + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "license": "MIT", + "peer": true + }, + "node_modules/statuses": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.1", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^10.4.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "license": "MIT", + "peer": true, + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uwebsockets-express": { + "version": "1.3.13", + "license": "MIT", + "peer": true, + "dependencies": { + "express": "^4.18.1", + "http-status-codes": "^2.1.4", + "mime": "^2.5.2", + "path-to-regexp": "^0.1.7" + } + }, + "node_modules/uwebsockets-express/node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/body-parser": { + "version": "1.20.3", + "license": "MIT", + "peer": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/uwebsockets-express/node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/cookie": { + "version": "0.7.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT", + "peer": true + }, + "node_modules/uwebsockets-express/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/uwebsockets-express/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "license": "MIT", + "peer": true + }, + "node_modules/uwebsockets-express/node_modules/express": { + "version": "4.21.2", + "license": "MIT", + "peer": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/uwebsockets-express/node_modules/finalhandler": { + "version": "1.3.1", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uwebsockets-express/node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uwebsockets-express/node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/merge-descriptors": { + "version": "1.0.3", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uwebsockets-express/node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uwebsockets-express/node_modules/path-to-regexp": { + "version": "0.1.12", + "license": "MIT", + "peer": true + }, + "node_modules/uwebsockets-express/node_modules/qs": { + "version": "6.13.0", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uwebsockets-express/node_modules/raw-body": { + "version": "2.5.2", + "license": "MIT", + "peer": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uwebsockets-express/node_modules/send": { + "version": "0.19.0", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uwebsockets-express/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uwebsockets-express/node_modules/send/node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "peer": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uwebsockets-express/node_modules/serve-static": { + "version": "1.16.2", + "license": "MIT", + "peer": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uwebsockets-express/node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uwebsockets-express/node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "peer": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uWebSockets.js": { + "version": "20.49.0", + "resolved": "git+ssh://git@github.com/uNetworking/uWebSockets.js.git#442087c0a01bf146acb7386910739ec81df06700", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..c961e64 --- /dev/null +++ b/server/package.json @@ -0,0 +1,25 @@ +{ + "name": "snatchgame-server", + "version": "1.0.0", + "description": "Competitive clicker game server", + "main": "dist/index.js", + "scripts": { + "dev": "npx ts-node-dev --respawn --transpile-only src/index.ts", + "build": "tsc", + "start": "node dist/index.js" + }, + "dependencies": { + "@colyseus/monitor": "latest", + "@colyseus/schema": "latest", + "colyseus": "latest", + "cors": "latest", + "express": "latest" + }, + "devDependencies": { + "@types/cors": "latest", + "@types/express": "latest", + "@types/node": "latest", + "ts-node-dev": "^2.0.0", + "typescript": "latest" + } +} diff --git a/server/src/adminApi.ts b/server/src/adminApi.ts new file mode 100644 index 0000000..5b48f61 --- /dev/null +++ b/server/src/adminApi.ts @@ -0,0 +1,129 @@ +import { Request, Response, Router } from "express"; +import { matchMaker } from "colyseus"; +import { GameRoom } from "./rooms/GameRoom"; + +const adminRouter = Router(); + +adminRouter.get("/rooms", async (req: Request, res: Response) => { + try { + const rooms = await matchMaker.query({}); + const roomStats = rooms.map(room => ({ + roomId: room.roomId, + name: room.name, + clients: room.clients, + maxClients: room.maxClients, + metadata: room.metadata, + locked: room.locked, + private: room.private, + createdAt: room.createdAt + })); + + res.json(roomStats); + } catch (error) { + console.error("[AdminAPI] Error fetching rooms:", error); + res.status(500).json({ error: "Failed to fetch rooms" }); + } +}); + +adminRouter.get("/rooms/:roomId/stats", async (req: Request, res: Response) => { + try { + const { roomId } = req.params; + const roomData = await matchMaker.remoteRoomCall(roomId, "getState"); + + res.json(roomData); + } catch (error) { + console.error(`[AdminAPI] Error fetching room ${req.params.roomId} stats:`, error); + res.status(500).json({ error: "Failed to fetch room stats" }); + } +}); + +adminRouter.post("/rooms/:roomId/pause", async (req: Request, res: Response) => { + try { + const { roomId } = req.params; + const rooms = await matchMaker.query({ roomId }); + + if (rooms.length === 0) { + return res.status(404).json({ error: "Room not found" }); + } + + await matchMaker.remoteRoomCall(roomId, "broadcast", ["admin:pause"]); + + res.json({ success: true, message: "Room paused" }); + } catch (error) { + console.error(`[AdminAPI] Error pausing room ${req.params.roomId}:`, error); + res.status(500).json({ error: "Failed to pause room" }); + } +}); + +adminRouter.post("/rooms/:roomId/resume", async (req: Request, res: Response) => { + try { + const { roomId } = req.params; + const rooms = await matchMaker.query({ roomId }); + + if (rooms.length === 0) { + return res.status(404).json({ error: "Room not found" }); + } + + await matchMaker.remoteRoomCall(roomId, "broadcast", ["admin:resume"]); + + res.json({ success: true, message: "Room resumed" }); + } catch (error) { + console.error(`[AdminAPI] Error resuming room ${req.params.roomId}:`, error); + res.status(500).json({ error: "Failed to resume room" }); + } +}); + +adminRouter.post("/rooms/:roomId/restart", async (req: Request, res: Response) => { + try { + const { roomId } = req.params; + const rooms = await matchMaker.query({ roomId }); + + if (rooms.length === 0) { + return res.status(404).json({ error: "Room not found" }); + } + + await matchMaker.remoteRoomCall(roomId, "broadcast", ["admin:restart"]); + + res.json({ success: true, message: "Room restarted" }); + } catch (error) { + console.error(`[AdminAPI] Error restarting room ${req.params.roomId}:`, error); + res.status(500).json({ error: "Failed to restart room" }); + } +}); + +adminRouter.post("/rooms/:roomId/kick/:playerId", async (req: Request, res: Response) => { + try { + const { roomId, playerId } = req.params; + const rooms = await matchMaker.query({ roomId }); + + if (rooms.length === 0) { + return res.status(404).json({ error: "Room not found" }); + } + + await matchMaker.remoteRoomCall(roomId, "broadcast", ["admin:kick", playerId]); + + res.json({ success: true, message: `Player ${playerId} kicked` }); + } catch (error) { + console.error(`[AdminAPI] Error kicking player from room ${req.params.roomId}:`, error); + res.status(500).json({ error: "Failed to kick player" }); + } +}); + +adminRouter.get("/stats", async (req: Request, res: Response) => { + try { + const stats = await matchMaker.stats.fetchAll(); + const globalCCU = await matchMaker.stats.getGlobalCCU(); + + res.json({ + processes: stats, + globalCCU, + localCCU: matchMaker.stats.local.ccu, + localRoomCount: matchMaker.stats.local.roomCount + }); + } catch (error) { + console.error("[AdminAPI] Error fetching stats:", error); + res.status(500).json({ error: "Failed to fetch stats" }); + } +}); + +export { adminRouter }; \ No newline at end of file diff --git a/server/src/index.ts b/server/src/index.ts new file mode 100644 index 0000000..6860592 --- /dev/null +++ b/server/src/index.ts @@ -0,0 +1,42 @@ +import { Server } from "colyseus"; +import { createServer } from "http"; +import express from "express"; +import cors from "cors"; +import { monitor } from "@colyseus/monitor"; + +import { GameRoom } from "./rooms/GameRoom"; +import { LobbyRoom } from "./rooms/LobbyRoom"; +import { adminRouter } from "./adminApi"; + +const port = Number(process.env.PORT) || 3000; +const app = express(); + +app.use(cors()); +app.use(express.json()); + +const server = createServer(app); +const gameServer = new Server({ + server, +}); + +gameServer.define("lobby", LobbyRoom) + .filterBy(["maxClients"]); + +gameServer.define("game", GameRoom) + .filterBy(["maxClients"]) + .enableRealtimeListing(); + +app.use("/api", adminRouter); + +app.use("/colyseus", monitor()); + +app.get("/health", (req, res) => { + res.json({ status: "healthy", uptime: process.uptime() }); +}); + +gameServer.listen(port); + +console.log(`🎮 Snatch Game Server is running on port ${port}`); +console.log(`📊 Monitor: http://localhost:${port}/colyseus`); +console.log(`🌐 WebSocket: ws://localhost:${port}`); +console.log(`🔧 Admin API: http://localhost:${port}/api`); \ No newline at end of file diff --git a/server/src/rooms/GameRoom.ts b/server/src/rooms/GameRoom.ts new file mode 100644 index 0000000..0080f4d --- /dev/null +++ b/server/src/rooms/GameRoom.ts @@ -0,0 +1,200 @@ +import { Room, Client } from "colyseus"; +import { GameState } from "./schemas/GameState"; +import { GameStatus } from "../../../shared/types"; +import { NameManager } from "../utils/nameManager"; + +export class GameRoom extends Room { + maxClients = 2; + private gameInterval?: NodeJS.Timeout; + private readonly TICK_RATE = 1000; // Update every second + + onCreate(options: any) { + this.setState(new GameState()); + this.state.roomId = this.roomId; + + this.onMessage("click", (client) => { + this.handleClick(client); + }); + + this.onMessage("admin:pause", () => { + this.state.pauseGame(); + }); + + this.onMessage("admin:resume", () => { + this.state.resumeGame(); + }); + + this.onMessage("admin:restart", () => { + this.handleRestart(); + }); + + this.onMessage("admin:kick", (client, playerId: string) => { + this.handleKick(playerId); + }); + } + + onJoin(client: Client, options: any) { + console.log(`[GameRoom] ${client.sessionId} joined room ${this.roomId}`); + + const playerName = options.playerName || "player"; + const uniqueName = NameManager.getInstance().generateUniquePlayerName(playerName, client.sessionId); + + this.state.addPlayer(client.sessionId, uniqueName); + + client.send("playerInfo", { + sessionId: client.sessionId, + name: uniqueName, + roomId: this.roomId + }); + + if (this.state.players.size === 2 && this.state.gameStatus === GameStatus.WAITING) { + this.startGame(); + } + } + + onLeave(client: Client, consented: boolean) { + console.log(`[GameRoom] ${client.sessionId} left room ${this.roomId}`); + + const player = this.state.players.get(client.sessionId); + if (player) { + player.connected = false; + NameManager.getInstance().releasePlayerName(client.sessionId); + } + + if (this.state.gameStatus === GameStatus.PLAYING) { + if (this.getConnectedPlayersCount() < 2) { + this.pauseGame(); + } + } + + this.allowReconnection(client, 30); + } + + async onReconnect(client: Client) { + console.log(`[GameRoom] ${client.sessionId} reconnected to room ${this.roomId}`); + + const player = this.state.players.get(client.sessionId); + if (player) { + player.connected = true; + } + + if (this.state.gameStatus === GameStatus.PAUSED && this.getConnectedPlayersCount() === 2) { + this.state.resumeGame(); + } + } + + onDispose() { + console.log(`[GameRoom] Room ${this.roomId} disposing...`); + + if (this.gameInterval) { + clearInterval(this.gameInterval); + } + + this.state.players.forEach(player => { + NameManager.getInstance().releasePlayerName(player.sessionId); + }); + } + + private startGame() { + console.log(`[GameRoom] Starting game in room ${this.roomId}`); + + this.state.startGame(); + this.broadcast("gameStart"); + + this.gameInterval = setInterval(() => { + this.state.updateTimer(this.TICK_RATE / 1000); + + if (this.state.gameStatus === GameStatus.FINISHED) { + this.endGame(); + } + }, this.TICK_RATE); + } + + private pauseGame() { + console.log(`[GameRoom] Pausing game in room ${this.roomId}`); + this.state.pauseGame(); + this.broadcast("gamePaused"); + } + + private endGame() { + console.log(`[GameRoom] Game ended in room ${this.roomId}. Winner: ${this.state.winner}`); + + if (this.gameInterval) { + clearInterval(this.gameInterval); + this.gameInterval = undefined; + } + + this.broadcast("gameEnd", { + winner: this.state.winner, + players: Array.from(this.state.players.values()).map(p => ({ + name: p.name, + clicks: p.clicks + })) + }); + + setTimeout(() => { + this.state.restartGame(); + if (this.state.players.size === 2) { + this.startGame(); + } + }, 5000); + } + + private handleClick(client: Client) { + if (this.state.gameStatus !== GameStatus.PLAYING) { + return; + } + + const player = this.state.players.get(client.sessionId); + if (player && player.connected) { + player.incrementClicks(); + } + } + + private handleRestart() { + console.log(`[GameRoom] Admin restart in room ${this.roomId}`); + + if (this.gameInterval) { + clearInterval(this.gameInterval); + this.gameInterval = undefined; + } + + this.state.restartGame(); + this.broadcast("gameRestart"); + + if (this.state.players.size === 2) { + setTimeout(() => this.startGame(), 2000); + } + } + + private handleKick(playerId: string) { + console.log(`[GameRoom] Admin kick player ${playerId} from room ${this.roomId}`); + + const client = this.clients.find(c => c.sessionId === playerId); + if (client) { + client.leave(1000); + } + } + + private getConnectedPlayersCount(): number { + let count = 0; + this.state.players.forEach(player => { + if (player.connected) count++; + }); + return count; + } + + getState() { + return { + roomId: this.roomId, + players: Array.from(this.state.players.values()).map(p => ({ + sessionId: p.sessionId, + name: p.name, + clicks: p.clicks + })), + gameStatus: this.state.gameStatus, + timeRemaining: this.state.timeRemaining, + winner: this.state.winner + }; + } +} \ No newline at end of file diff --git a/server/src/rooms/LobbyRoom.ts b/server/src/rooms/LobbyRoom.ts new file mode 100644 index 0000000..fcfae4b --- /dev/null +++ b/server/src/rooms/LobbyRoom.ts @@ -0,0 +1,157 @@ +import { Room, Client, matchMaker } from "colyseus"; +import { LobbyState, AvailableRoom } from "./schemas/LobbyState"; +import { NameManager } from "../utils/nameManager"; + +export class LobbyRoom extends Room { + private updateInterval?: NodeJS.Timeout; + + onCreate(options: any) { + this.setState(new LobbyState()); + this.setPrivate(false); + + this.onMessage("setName", (client, playerName: string) => { + this.handleSetName(client, playerName); + }); + + this.onMessage("quickPlay", (client) => { + this.handleQuickPlay(client); + }); + + this.onMessage("joinRoom", (client, roomId: string) => { + this.handleJoinRoom(client, roomId); + }); + + this.updateInterval = setInterval(() => { + this.updateAvailableRooms(); + }, 2000); + } + + onJoin(client: Client, options: any) { + console.log(`[LobbyRoom] ${client.sessionId} joined lobby`); + + const defaultName = `guest`; + const uniqueName = NameManager.getInstance().generateUniquePlayerName(defaultName, client.sessionId); + + this.state.addPlayer(client.sessionId, uniqueName); + + client.send("welcome", { + sessionId: client.sessionId, + assignedName: uniqueName + }); + + this.updateAvailableRooms(); + } + + onLeave(client: Client, consented: boolean) { + console.log(`[LobbyRoom] ${client.sessionId} left lobby`); + + const player = this.state.players.get(client.sessionId); + if (player) { + NameManager.getInstance().releasePlayerName(client.sessionId); + } + + this.state.removePlayer(client.sessionId); + } + + onDispose() { + console.log("[LobbyRoom] Disposing lobby room"); + + if (this.updateInterval) { + clearInterval(this.updateInterval); + } + + this.state.players.forEach(player => { + NameManager.getInstance().releasePlayerName(player.sessionId); + }); + } + + private handleSetName(client: Client, playerName: string) { + const currentPlayer = this.state.players.get(client.sessionId); + if (!currentPlayer) return; + + NameManager.getInstance().releasePlayerName(client.sessionId); + + const uniqueName = NameManager.getInstance().generateUniquePlayerName(playerName, client.sessionId); + + currentPlayer.name = uniqueName; + + client.send("nameUpdated", { + name: uniqueName + }); + } + + private async handleQuickPlay(client: Client) { + const player = this.state.players.get(client.sessionId); + if (!player || player.inGame) return; + + try { + const reservation = await matchMaker.joinOrCreate("game", { + playerName: player.name + }); + + this.state.setPlayerInGame(client.sessionId, true); + + client.send("roomReservation", { + sessionId: reservation.sessionId, + room: reservation.room + }); + + setTimeout(() => { + client.leave(); + }, 1000); + + } catch (error) { + console.error("[LobbyRoom] Error in quick play:", error); + client.send("error", { + message: "Could not find or create a game room" + }); + } + } + + private async handleJoinRoom(client: Client, roomId: string) { + const player = this.state.players.get(client.sessionId); + if (!player || player.inGame) return; + + try { + const reservation = await matchMaker.joinById(roomId, { + playerName: player.name + }); + + this.state.setPlayerInGame(client.sessionId, true); + + client.send("roomReservation", { + sessionId: reservation.sessionId, + room: reservation.room + }); + + setTimeout(() => { + client.leave(); + }, 1000); + + } catch (error) { + console.error("[LobbyRoom] Error joining room:", error); + client.send("error", { + message: "Could not join the selected room" + }); + } + } + + private async updateAvailableRooms() { + try { + const rooms = await matchMaker.query({ name: "game" }); + + const availableRooms = rooms + .filter(room => !room.locked && room.clients < 2) + .map(room => new AvailableRoom( + room.roomId, + room.clients, + room.metadata?.gameStatus || "waiting" + )); + + this.state.updateAvailableRooms(availableRooms); + + } catch (error) { + console.error("[LobbyRoom] Error updating available rooms:", error); + } + } +} \ No newline at end of file diff --git a/server/src/rooms/schemas/GameState.ts b/server/src/rooms/schemas/GameState.ts new file mode 100644 index 0000000..605010f --- /dev/null +++ b/server/src/rooms/schemas/GameState.ts @@ -0,0 +1,86 @@ +import { Schema, type, MapSchema } from "@colyseus/schema"; +import { Player } from "./Player"; +import { GameStatus } from "../../../../shared/types"; + +export class GameState extends Schema { + @type({ map: Player }) players = new MapSchema(); + @type("string") gameStatus: GameStatus = GameStatus.WAITING; + @type("number") timeRemaining: number = 600; // 10 minutes in seconds + @type("string") winner: string = ""; + @type("number") startTime: number = 0; + @type("string") roomId: string = ""; + + constructor() { + super(); + } + + addPlayer(sessionId: string, name: string): Player { + const player = new Player(sessionId, name); + this.players.set(sessionId, player); + return player; + } + + removePlayer(sessionId: string): void { + this.players.delete(sessionId); + } + + startGame(): void { + this.gameStatus = GameStatus.PLAYING; + this.startTime = Date.now(); + this.timeRemaining = 600; + this.resetAllPlayers(); + } + + pauseGame(): void { + if (this.gameStatus === GameStatus.PLAYING) { + this.gameStatus = GameStatus.PAUSED; + } + } + + resumeGame(): void { + if (this.gameStatus === GameStatus.PAUSED) { + this.gameStatus = GameStatus.PLAYING; + } + } + + finishGame(): void { + this.gameStatus = GameStatus.FINISHED; + this.determineWinner(); + } + + restartGame(): void { + this.gameStatus = GameStatus.WAITING; + this.timeRemaining = 600; + this.winner = ""; + this.startTime = 0; + this.resetAllPlayers(); + } + + private resetAllPlayers(): void { + this.players.forEach(player => player.reset()); + } + + private determineWinner(): void { + let maxClicks = -1; + let winner = ""; + + this.players.forEach(player => { + if (player.clicks > maxClicks) { + maxClicks = player.clicks; + winner = player.name; + } + }); + + this.winner = winner; + } + + updateTimer(deltaTime: number): void { + if (this.gameStatus === GameStatus.PLAYING && this.timeRemaining > 0) { + this.timeRemaining -= deltaTime; + if (this.timeRemaining <= 0) { + this.timeRemaining = 0; + this.finishGame(); + } + } + } +} \ No newline at end of file diff --git a/server/src/rooms/schemas/LobbyState.ts b/server/src/rooms/schemas/LobbyState.ts new file mode 100644 index 0000000..8d19572 --- /dev/null +++ b/server/src/rooms/schemas/LobbyState.ts @@ -0,0 +1,61 @@ +import { Schema, type, MapSchema, ArraySchema } from "@colyseus/schema"; + +export class LobbyPlayer extends Schema { + @type("string") sessionId: string = ""; + @type("string") name: string = ""; + @type("boolean") inGame: boolean = false; + + constructor(sessionId: string, name: string) { + super(); + this.sessionId = sessionId; + this.name = name; + this.inGame = false; + } +} + +export class AvailableRoom extends Schema { + @type("string") roomId: string = ""; + @type("number") playerCount: number = 0; + @type("string") status: string = ""; + + constructor(roomId: string, playerCount: number, status: string) { + super(); + this.roomId = roomId; + this.playerCount = playerCount; + this.status = status; + } +} + +export class LobbyState extends Schema { + @type({ map: LobbyPlayer }) players = new MapSchema(); + @type([AvailableRoom]) availableRooms = new ArraySchema(); + @type("number") totalPlayers: number = 0; + + constructor() { + super(); + } + + addPlayer(sessionId: string, name: string): LobbyPlayer { + const player = new LobbyPlayer(sessionId, name); + this.players.set(sessionId, player); + this.totalPlayers = this.players.size; + return player; + } + + removePlayer(sessionId: string): void { + this.players.delete(sessionId); + this.totalPlayers = this.players.size; + } + + updateAvailableRooms(rooms: AvailableRoom[]): void { + this.availableRooms.clear(); + rooms.forEach(room => this.availableRooms.push(room)); + } + + setPlayerInGame(sessionId: string, inGame: boolean): void { + const player = this.players.get(sessionId); + if (player) { + player.inGame = inGame; + } + } +} \ No newline at end of file diff --git a/server/src/rooms/schemas/Player.ts b/server/src/rooms/schemas/Player.ts new file mode 100644 index 0000000..5237385 --- /dev/null +++ b/server/src/rooms/schemas/Player.ts @@ -0,0 +1,24 @@ +import { Schema, type } from "@colyseus/schema"; + +export class Player extends Schema { + @type("string") sessionId: string = ""; + @type("string") name: string = ""; + @type("number") clicks: number = 0; + @type("boolean") connected: boolean = true; + + constructor(sessionId: string, name: string) { + super(); + this.sessionId = sessionId; + this.name = name; + this.clicks = 0; + this.connected = true; + } + + incrementClicks(): void { + this.clicks++; + } + + reset(): void { + this.clicks = 0; + } +} \ No newline at end of file diff --git a/server/src/utils/nameManager.ts b/server/src/utils/nameManager.ts new file mode 100644 index 0000000..b23a406 --- /dev/null +++ b/server/src/utils/nameManager.ts @@ -0,0 +1,46 @@ +export class NameManager { + private static instance: NameManager; + private nameCounters: Map = new Map(); + private sessionToName: Map = new Map(); + + private constructor() {} + + static getInstance(): NameManager { + if (!NameManager.instance) { + NameManager.instance = new NameManager(); + } + return NameManager.instance; + } + + generateUniquePlayerName(baseName: string, sessionId: string): string { + const normalizedName = baseName.trim().toLowerCase(); + + if (!normalizedName) { + return this.generateUniquePlayerName('player', sessionId); + } + + const currentCounter = this.nameCounters.get(normalizedName) || 0; + const newCounter = currentCounter + 1; + this.nameCounters.set(normalizedName, newCounter); + + const uniqueName = newCounter === 1 ? normalizedName : `${normalizedName}-${newCounter}`; + this.sessionToName.set(sessionId, uniqueName); + + return uniqueName; + } + + releasePlayerName(sessionId: string): void { + const name = this.sessionToName.get(sessionId); + if (name) { + this.sessionToName.delete(sessionId); + } + } + + getPlayerName(sessionId: string): string | undefined { + return this.sessionToName.get(sessionId); + } + + getAllActivePlayers(): string[] { + return Array.from(this.sessionToName.values()); + } +} \ No newline at end of file diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 0000000..37d3b4e --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "../", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "experimentalDecorators": true, + "useDefineForClassFields": false, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*", "../shared/**/*"], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/shared/types.ts b/shared/types.ts new file mode 100644 index 0000000..46fb8b0 --- /dev/null +++ b/shared/types.ts @@ -0,0 +1,27 @@ +export interface PlayerInfo { + sessionId: string; + name: string; + clicks: number; +} + +export interface RoomStats { + roomId: string; + players: PlayerInfo[]; + gameStatus: GameStatus; + timeRemaining: number; + winner?: string; + createdAt: number; +} + +export enum GameStatus { + WAITING = 'waiting', + PLAYING = 'playing', + PAUSED = 'paused', + FINISHED = 'finished' +} + +export interface AdminAction { + action: 'pause' | 'restart' | 'kick'; + roomId: string; + playerId?: string; +} \ No newline at end of file