diff --git a/.env.example b/.env.example
index 83dcc1f..de16f2e 100644
--- a/.env.example
+++ b/.env.example
@@ -10,7 +10,7 @@ DB_HOST=localhost
DB_PORT=5432
DB_NAME=shattered_void_dev
DB_USER=postgres
-DB_PASSWORD=s5d7dfs5e2q23
+DB_PASSWORD=password
# Redis Configuration
REDIS_HOST=localhost
diff --git a/STARTUP_GUIDE.md b/STARTUP_GUIDE.md
deleted file mode 100644
index 51cfd8b..0000000
--- a/STARTUP_GUIDE.md
+++ /dev/null
@@ -1,568 +0,0 @@
-# Shattered Void MMO - Startup System Guide
-
-This guide covers the comprehensive startup system for the Shattered Void MMO, providing multiple ways to launch and manage the game services.
-
-## Quick Start
-
-The easiest way to start the game:
-
-```bash
-# Simple startup with all default settings
-./start.sh
-
-# Or using npm
-npm run game
-```
-
-## Startup Options
-
-### Shell Script (Recommended)
-
-The `start.sh` script provides the most user-friendly interface:
-
-```bash
-# Development mode (default)
-./start.sh
-
-# Production mode
-./start.sh --env production
-
-# Debug mode with verbose logging
-./start.sh --debug --verbose
-
-# Backend only (no frontend)
-./start.sh --no-frontend
-
-# Custom port
-./start.sh --port 8080
-
-# Skip database checks (useful for testing)
-./start.sh --no-database --skip-preflight
-```
-
-### NPM Scripts
-
-```bash
-# Comprehensive startup with full system validation
-npm run start:game
-
-# Environment-specific startup
-npm run start:dev # Development mode
-npm run start:prod # Production mode
-npm run start:staging # Staging mode
-
-# Quick startup (shell script)
-npm run start:quick
-
-# Debug mode
-npm run start:debug
-
-# Backend only
-npm run start:backend-only
-```
-
-### Direct Node.js
-
-```bash
-# Direct startup (bypasses some safety checks)
-node start-game.js
-
-# With environment
-NODE_ENV=production node start-game.js
-```
-
-## Configuration
-
-### Environment Variables
-
-The startup system respects these environment variables:
-
-```bash
-# Core settings
-NODE_ENV=development|production|staging|testing
-PORT=3000 # Backend port
-FRONTEND_PORT=5173 # Frontend port
-HOST=0.0.0.0 # Host binding
-
-# Service toggles
-ENABLE_FRONTEND=true|false # Enable/disable frontend
-DISABLE_DATABASE=true|false # Skip database
-DISABLE_REDIS=true|false # Skip Redis
-ENABLE_HEALTH_MONITORING=true|false # Health checks
-
-# Startup behavior
-SKIP_PREFLIGHT=true|false # Skip system checks
-VERBOSE_STARTUP=true|false # Detailed logging
-AUTO_MIGRATE=true|false # Auto-run migrations
-AUTO_SEED=true|false # Auto-run seeds
-
-# Visual settings
-DISABLE_BANNER=true|false # Hide startup banner
-DISABLE_COLORS=true|false # Disable colored output
-```
-
-### Configuration File
-
-Advanced configuration is available in `config/startup.config.js`:
-
-```javascript
-// Example: Custom timeout settings
-const config = {
- backend: {
- startupTimeout: 30000, // 30 seconds
- healthEndpoint: '/health'
- },
- database: {
- migrationTimeout: 60000, // 60 seconds
- autoMigrate: true
- },
- healthMonitoring: {
- interval: 30000, // 30 seconds
- alertThresholds: {
- responseTime: 5000, // 5 seconds
- memoryUsage: 80 // 80%
- }
- }
-};
-```
-
-## System Components
-
-### 1. Pre-flight Checks (`scripts/startup-checks.js`)
-
-Validates system requirements before startup:
-
-- ✅ Node.js version (18+)
-- ✅ NPM availability
-- ✅ Environment configuration
-- ✅ Directory structure
-- ✅ Package dependencies
-- ✅ Port availability
-- ✅ Database configuration
-- ✅ Redis configuration (optional)
-- ✅ Log directories
-- ✅ Frontend dependencies (optional)
-- ✅ System memory (1GB+ recommended)
-- ✅ Disk space (<90% usage)
-- ✅ File permissions
-
-Test individually:
-```bash
-npm run system:check
-```
-
-### 2. Database Validation (`scripts/database-validator.js`)
-
-Comprehensive database health checks:
-
-- 🔗 Connectivity testing
-- 📦 Migration status and auto-execution
-- 🏗️ Schema structure validation
-- 🌱 Seed data verification
-- 🔍 Data integrity checks
-- 📊 Performance metrics
-
-Test individually:
-```bash
-npm run db:validate
-```
-
-### 3. Health Monitoring (`scripts/health-monitor.js`)
-
-Real-time service monitoring:
-
-- 🏥 Service health checks
-- 📈 Performance metrics
-- 🚨 Alert system
-- 📊 Uptime tracking
-- 💾 System resource monitoring
-
-Test individually:
-```bash
-npm run health:check
-```
-
-### 4. Main Orchestrator (`start-game.js`)
-
-Central startup coordination:
-
-- 🎭 Phase-based startup
-- ⏱️ Timeout management
-- 🔄 Retry logic
-- 📝 Comprehensive logging
-- 🛑 Graceful shutdown
-- 📊 Performance metrics
-- 🔧 Node.js version compatibility detection
-- 📦 Automatic frontend fallback for older Node.js versions
-
-## Node.js Version Compatibility
-
-The system automatically detects Node.js version compatibility and handles Vite development server limitations:
-
-### Vite Development Server Requirements
-
-- **Node.js 20+**: Full Vite development server support
-- **Node.js 18-19**: Automatic fallback to built frontend static server
-- **Node.js <18**: Not supported
-
-### Automatic Fallback Behavior
-
-When Node.js version is incompatible with Vite 7.x (requires `crypto.hash()` from Node.js 20+):
-
-1. **Detection**: System detects Node.js version during startup
-2. **Warning**: Clear warning about version compatibility
-3. **Fallback**: Automatically serves built frontend from `/frontend/dist/`
-4. **Status**: Frontend shows as "static" mode in startup summary
-
-```bash
-# Example startup output with Node.js 18.x
-Node.js version: v18.19.1
-Node.js v18.19.1 is not compatible with Vite 7.x (requires Node.js 20+)
-crypto.hash() function is not available in this Node.js version
-Attempting to serve built frontend as fallback...
-Built frontend fallback started in 5ms
-
-║ ✅ Frontend:5173 (static) ║
-```
-
-### Configuration Options
-
-Control fallback behavior with environment variables:
-
-```bash
-# Disable frontend fallback (fail if Vite incompatible)
-FRONTEND_FALLBACK=false ./start.sh
-
-# Force use built frontend even with compatible Node.js
-# (automatically happens if Vite dev server fails for any reason)
-```
-
-### Building Frontend for Fallback
-
-Ensure built frontend is available:
-
-```bash
-# Build frontend for production/fallback use
-cd frontend
-npm run build
-
-# Verify build exists
-ls -la dist/
-```
-
-## Startup Phases
-
-The startup system follows these phases:
-
-1. **🔍 Pre-flight Checks** - System validation
-2. **🗄️ Database Validation** - DB connectivity and migrations
-3. **🖥️ Backend Server Startup** - Express server launch
-4. **🌐 Frontend Server Startup** - React dev server (if enabled)
-5. **🏥 Health Monitoring** - Service monitoring activation
-
-Each phase includes:
-- ⏱️ Timing metrics
-- 🔄 Retry logic
-- ❌ Error handling
-- 📊 Progress reporting
-
-## Service Management
-
-### Starting Services
-
-```bash
-# Full stack (backend + frontend + monitoring)
-./start.sh
-
-# Backend only
-./start.sh --no-frontend
-
-# Skip health monitoring
-./start.sh --no-health
-
-# Database-free mode (for testing)
-./start.sh --no-database
-```
-
-### Stopping Services
-
-```bash
-# Graceful shutdown
-Ctrl+C
-
-# Force stop (if needed)
-pkill -f start-game.js
-```
-
-### Service Status
-
-The startup system provides real-time status:
-
-```
-╔═══════════════════════════════════════════════════════════════╗
-║ STARTUP SUMMARY ║
-╠═══════════════════════════════════════════════════════════════╣
-║ Total Duration: 2847ms ║
-║ ║
-║ Services Status: ║
-║ ✅ Preflight ║
-║ ✅ Database ║
-║ ✅ Backend:3000 ║
-║ ✅ Frontend:5173 ║
-║ ✅ HealthMonitor ║
-╚═══════════════════════════════════════════════════════════════╝
-```
-
-## Troubleshooting
-
-### Common Issues
-
-1. **Port already in use**
- ```bash
- # Use different port
- ./start.sh --port 8080
-
- # Or kill existing process
- lsof -ti:3000 | xargs kill
- ```
-
-2. **Database connection failed**
- ```bash
- # Check PostgreSQL status
- sudo systemctl status postgresql
-
- # Start PostgreSQL
- sudo systemctl start postgresql
-
- # Create database
- createdb shattered_void_dev
- ```
-
-3. **Missing dependencies**
- ```bash
- # Install dependencies
- npm install
-
- # Install frontend dependencies
- cd frontend && npm install
- ```
-
-4. **Migration issues**
- ```bash
- # Reset database
- npm run db:reset
-
- # Manual migration
- npm run db:migrate
- ```
-
-5. **Vite development server fails (Node.js compatibility)**
- ```bash
- # Check Node.js version
- node --version
-
- # If Node.js < 20, system will automatically fallback
- # To upgrade Node.js:
- # Using nvm:
- nvm install 20
- nvm use 20
-
- # Using package manager:
- # Ubuntu/Debian: sudo apt update && sudo apt install nodejs
- # MacOS: brew install node@20
-
- # Verify fallback works by ensuring frontend is built:
- cd frontend && npm run build
- ```
-
-6. **Frontend fallback not working**
- ```bash
- # Ensure frontend is built
- cd frontend
- npm install
- npm run build
-
- # Verify build directory exists
- ls -la dist/
-
- # Check if Express is available (should be in package.json)
- npm list express
- ```
-
-### Debug Mode
-
-Enable comprehensive debugging:
-
-```bash
-# Maximum verbosity
-./start.sh --debug --verbose
-
-# Or with environment variables
-DEBUG=* VERBOSE_STARTUP=true ./start.sh
-```
-
-### Logs
-
-Access different log streams:
-
-```bash
-# Combined logs
-npm run logs
-
-# Error logs only
-npm run logs:error
-
-# Startup logs
-npm run logs:startup
-
-# Audit logs
-npm run logs:audit
-```
-
-### Health Check Endpoints
-
-Once running, access health information:
-
-```bash
-# Backend health
-curl http://localhost:3000/health
-
-# Health monitoring data (if debug endpoints enabled)
-curl http://localhost:3000/debug/health
-```
-
-## Production Deployment
-
-### Production Mode
-
-```bash
-# Production startup
-./start.sh --env production
-
-# Or with npm
-npm run start:prod
-```
-
-Production mode changes:
-- 🚫 Frontend disabled (serves pre-built assets)
-- ⚡ Faster health check intervals
-- 🔒 Enhanced security checks
-- 📊 Performance monitoring enabled
-- 🚨 Stricter error handling
-
-### Environment Variables for Production
-
-```bash
-NODE_ENV=production
-DISABLE_FRONTEND=true # Use nginx/CDN for frontend
-ENABLE_HEALTH_MONITORING=true
-LOG_LEVEL=warn
-CRASH_REPORTING=true
-PERFORMANCE_REPORTING=true
-```
-
-### Docker Integration
-
-The startup system works with Docker:
-
-```bash
-# Build Docker image
-npm run docker:build
-
-# Run with Docker Compose
-npm run docker:run
-```
-
-## Development Tips
-
-### Quick Development Cycle
-
-```bash
-# Fast startup without full checks
-SKIP_PREFLIGHT=true ./start.sh --no-frontend
-
-# Backend only with auto-restart
-npm run dev
-```
-
-### Testing the Startup System
-
-```bash
-# Test all components
-npm run system:check # Pre-flight checks
-npm run db:validate # Database validation
-npm run health:check # Health monitoring
-
-# Test specific scenarios
-./start.sh --no-database --skip-preflight # Minimal startup
-./start.sh --debug --log-file startup.log # Full logging
-```
-
-### Customizing the Startup
-
-Modify `config/startup.config.js` for custom behavior:
-
-```javascript
-module.exports = {
- backend: {
- startupTimeout: 45000, // Longer timeout
- port: 8080 // Different default port
- },
- preflightChecks: {
- enabled: false // Skip checks in development
- }
-};
-```
-
-## API Reference
-
-### Startup Script Options
-
-| Option | Environment Variable | Description |
-|--------|---------------------|-------------|
-| `--env ENV` | `NODE_ENV` | Set environment mode |
-| `--port PORT` | `PORT` | Backend server port |
-| `--frontend-port PORT` | `FRONTEND_PORT` | Frontend server port |
-| `--no-frontend` | `ENABLE_FRONTEND=false` | Disable frontend |
-| `--no-health` | `ENABLE_HEALTH_MONITORING=false` | Disable health monitoring |
-| `--no-database` | `DISABLE_DATABASE=true` | Skip database |
-| `--no-redis` | `DISABLE_REDIS=true` | Skip Redis |
-| `--skip-preflight` | `SKIP_PREFLIGHT=true` | Skip system checks |
-| `--verbose` | `VERBOSE_STARTUP=true` | Enable verbose logging |
-| `--debug` | `DEBUG=*` | Enable debug mode |
-| `--no-colors` | `DISABLE_COLORS=true` | Disable colored output |
-
-### NPM Scripts Reference
-
-| Script | Description |
-|--------|-------------|
-| `npm run game` | Quick startup (shell script) |
-| `npm run start:game` | Full startup with validation |
-| `npm run start:dev` | Development mode |
-| `npm run start:prod` | Production mode |
-| `npm run start:debug` | Debug mode |
-| `npm run start:backend-only` | Backend only |
-| `npm run system:check` | Run system checks |
-| `npm run health:check` | Test health monitoring |
-| `npm run db:validate` | Validate database |
-
-## Contributing
-
-When modifying the startup system:
-
-1. **Test all scenarios** - Test with different combinations of flags
-2. **Update documentation** - Keep this guide current
-3. **Maintain backward compatibility** - Don't break existing workflows
-4. **Add comprehensive logging** - Help with debugging
-5. **Follow error handling patterns** - Use the established error classes
-
-The startup system is designed to be:
-- 🛡️ **Robust** - Handles failures gracefully
-- 🔧 **Configurable** - Adapts to different environments
-- 📊 **Observable** - Provides comprehensive monitoring
-- 🚀 **Fast** - Optimized startup performance
-- 🎯 **User-friendly** - Clear interface and error messages
-
----
-
-For more information, see the individual component documentation or run `./start.sh --help`.
\ No newline at end of file
diff --git a/TESTING_GUIDE.md b/TESTING_GUIDE.md
deleted file mode 100644
index de3ade0..0000000
--- a/TESTING_GUIDE.md
+++ /dev/null
@@ -1,157 +0,0 @@
-# Shattered Void MMO - Testing Guide
-
-## Current Status: READY FOR TESTING! 🎉
-
-The Shattered Void MMO is now **fully functional** with both backend and frontend implemented. Here's how to test it:
-
-## Backend Server ✅ RUNNING
-
-**Status**: ✅ **OPERATIONAL** on port 3000
-- **URL**: http://localhost:3000
-- **API**: http://localhost:3000/api/
-- **WebSocket**: ws://localhost:3000
-- **Database**: PostgreSQL (currently disabled for testing)
-- **Redis**: Not required (using in-memory fallback)
-
-### Backend Features Available:
-- Complete REST API with 99+ endpoints
-- Real-time WebSocket events
-- Authentication system (JWT tokens)
-- Colony management system
-- Resource production automation
-- Fleet management system
-- Research system with technology tree
-- Combat system with plugin architecture
-
-## Frontend Application ✅ BUILT
-
-**Status**: ✅ **BUILT AND READY**
-- **Location**: `/frontend/dist/` (production build)
-- **Technology**: React 18 + TypeScript + Tailwind CSS
-- **Features**: Authentication, Colony Management, Real-time Updates
-
-### Frontend Features Available:
-- User registration and login
-- Colony dashboard with real-time resource tracking
-- Fleet management interface
-- Research tree visualization
-- WebSocket integration for live updates
-- Mobile-responsive design
-
-## How to Test
-
-### Option 1: Direct API Testing
-Test the backend API directly:
-
-```bash
-# Test API status
-curl http://localhost:3000/api/
-
-# Test user registration
-curl -X POST http://localhost:3000/api/auth/register \
- -H "Content-Type: application/json" \
- -d '{
- "email": "test@example.com",
- "username": "testplayer",
- "password": "TestPassword123!"
- }'
-
-# Test login
-curl -X POST http://localhost:3000/api/auth/login \
- -H "Content-Type: application/json" \
- -d '{
- "email": "test@example.com",
- "password": "TestPassword123!"
- }'
-```
-
-### Option 2: Frontend Testing (Recommended)
-
-The frontend is built and ready to serve. To test the full application:
-
-1. **Serve the Frontend**:
- ```bash
- cd /home/megaproxy/claude/galaxygame/frontend/dist
- python3 -m http.server 5173
- ```
-
-2. **Access the Application**:
- - Open browser to: http://localhost:5173
- - Register a new account
- - Create colonies and manage resources
- - Experience real-time updates
-
-### Option 3: Node.js Frontend Development (Requires Node.js 20+)
-
-If you have Node.js 20+:
-```bash
-cd /home/megaproxy/claude/galaxygame/frontend
-npm run dev
-```
-
-## Testing Scenarios
-
-### 1. Authentication Flow
-- ✅ Register new user account
-- ✅ Login with credentials
-- ✅ JWT token management
-- ✅ Protected route access
-
-### 2. Colony Management
-- ✅ Create new colonies at galaxy coordinates
-- ✅ View colony list with real-time updates
-- ✅ Monitor resource production
-- ✅ Build structures and upgrades
-
-### 3. Real-time Features
-- ✅ WebSocket connection status
-- ✅ Live resource counters
-- ✅ Real-time game event notifications
-- ✅ Automatic UI updates
-
-### 4. Fleet Operations
-- ✅ Create fleets with ship designs
-- ✅ Move fleets between colonies
-- ✅ Fleet combat engagement
-- ✅ Ship construction and management
-
-### 5. Research System
-- ✅ View technology tree
-- ✅ Start research projects
-- ✅ Technology unlocks and bonuses
-- ✅ Research facility management
-
-## Current Capabilities
-
-### ✅ Fully Implemented Systems:
-- **Authentication**: Complete with email verification, password reset
-- **Colony Management**: Full colony creation, building, resource management
-- **Fleet System**: Ship designs, fleet creation, movement, combat ready
-- **Research System**: Technology tree with 23+ technologies
-- **Combat System**: Plugin-based combat with multiple resolution types
-- **Real-time Updates**: WebSocket events for all game actions
-- **Game Automation**: 60-second tick system processing all players
-- **Admin Tools**: Complete admin API for game management
-
-### 🚀 Ready for Multiplayer Testing:
-- Supports 100+ concurrent users
-- Real-time multiplayer interactions
-- Persistent game state
-- Automated game progression
-
-## Notes
-
-- **Database**: Currently using file-based storage for easy testing
-- **Redis**: Using in-memory fallback (no Redis installation required)
-- **Email**: Development mode (emails logged to console)
-- **Node.js**: Backend works with Node.js 18+, frontend build works universally
-
-## Next Steps
-
-1. **Test basic registration and login**
-2. **Create colonies and explore the galaxy**
-3. **Experience real-time resource production**
-4. **Build fleets and engage in combat**
-5. **Research technologies and unlock new capabilities**
-
-The game is fully playable and ready for community testing! 🎮
\ No newline at end of file
diff --git a/config/startup.config.js b/config/startup.config.js
deleted file mode 100644
index b1a4f7f..0000000
--- a/config/startup.config.js
+++ /dev/null
@@ -1,380 +0,0 @@
-/**
- * Shattered Void MMO - Startup Configuration
- *
- * Central configuration file for the startup system, allowing easy customization
- * of startup behavior, timeouts, and service settings.
- */
-
-const path = require('path');
-
-/**
- * Default startup configuration
- */
-const defaultConfig = {
- // Environment settings
- environment: {
- mode: process.env.NODE_ENV || 'development',
- logLevel: process.env.LOG_LEVEL || 'info',
- enableDebug: process.env.NODE_ENV === 'development'
- },
-
- // Backend server configuration
- backend: {
- port: parseInt(process.env.PORT) || 3000,
- host: process.env.HOST || '0.0.0.0',
- script: 'src/server.js',
- startupTimeout: 30000,
- healthEndpoint: '/health',
- gracefulShutdownTimeout: 10000
- },
-
- // Frontend configuration
- frontend: {
- enabled: process.env.ENABLE_FRONTEND !== 'false',
- port: parseInt(process.env.FRONTEND_PORT) || 5173,
- host: process.env.FRONTEND_HOST || '0.0.0.0',
- directory: './frontend',
- buildDirectory: './frontend/dist',
- startupTimeout: 45000,
- buildTimeout: 120000,
- devCommand: 'dev',
- buildCommand: 'build',
- previewCommand: 'preview'
- },
-
- // Database configuration
- database: {
- enabled: process.env.DISABLE_DATABASE !== 'true',
- connectionTimeout: 10000,
- migrationTimeout: 60000,
- seedTimeout: 30000,
- autoMigrate: process.env.AUTO_MIGRATE !== 'false',
- autoSeed: process.env.AUTO_SEED === 'true',
- integrityChecks: process.env.SKIP_DB_INTEGRITY !== 'true',
- retryAttempts: 3,
- retryDelay: 2000
- },
-
- // Redis configuration
- redis: {
- enabled: process.env.DISABLE_REDIS !== 'true',
- optional: true,
- connectionTimeout: 5000,
- retryAttempts: 3,
- retryDelay: 1000,
- host: process.env.REDIS_HOST || 'localhost',
- port: parseInt(process.env.REDIS_PORT) || 6379
- },
-
- // Health monitoring configuration
- healthMonitoring: {
- enabled: process.env.ENABLE_HEALTH_MONITORING !== 'false',
- interval: parseInt(process.env.HEALTH_CHECK_INTERVAL) || 30000,
- timeout: 5000,
- alertThresholds: {
- responseTime: 5000,
- memoryUsage: 80,
- cpuUsage: 90,
- errorRate: 10,
- consecutiveFailures: 3
- },
- systemMetricsInterval: 10000,
- historySize: 100
- },
-
- // Startup process configuration
- startup: {
- maxRetries: parseInt(process.env.STARTUP_MAX_RETRIES) || 3,
- retryDelay: parseInt(process.env.STARTUP_RETRY_DELAY) || 2000,
- enableBanner: process.env.DISABLE_BANNER !== 'true',
- enableColors: process.env.DISABLE_COLORS !== 'true',
- verboseLogging: process.env.VERBOSE_STARTUP === 'true',
- failFast: process.env.FAIL_FAST === 'true',
- gracefulShutdown: true
- },
-
- // Pre-flight checks configuration
- preflightChecks: {
- enabled: process.env.SKIP_PREFLIGHT !== 'true',
- timeout: 30000,
- required: {
- nodeVersion: true,
- npmAvailability: true,
- environmentConfig: true,
- directoryStructure: true,
- packageDependencies: true,
- portAvailability: true,
- databaseConfig: true,
- logDirectories: true,
- filePermissions: true,
- systemMemory: true,
- diskSpace: true
- },
- optional: {
- redisConfig: true,
- frontendDependencies: true
- },
- requirements: {
- nodeMinVersion: 18,
- memoryMinGB: 1,
- diskSpaceMaxUsage: 90
- }
- },
-
- // Logging configuration
- logging: {
- level: process.env.LOG_LEVEL || 'info',
- colorize: process.env.DISABLE_COLORS !== 'true',
- timestamp: true,
- includeProcessId: true,
- startupLog: true,
- errorStackTrace: process.env.NODE_ENV === 'development'
- },
-
- // Performance configuration
- performance: {
- measureStartupTime: true,
- measurePhaseTime: true,
- memoryMonitoring: true,
- cpuMonitoring: process.env.NODE_ENV === 'development',
- performanceReporting: process.env.PERFORMANCE_REPORTING === 'true'
- },
-
- // Security configuration
- security: {
- hidePasswords: true,
- sanitizeEnvironment: true,
- validatePorts: true,
- checkFilePermissions: true
- },
-
- // Development specific settings
- development: {
- hotReload: true,
- autoRestart: process.env.AUTO_RESTART === 'true',
- debugEndpoints: process.env.ENABLE_DEBUG_ENDPOINTS === 'true',
- verboseErrors: true,
- showDeprecations: true
- },
-
- // Production specific settings
- production: {
- compressionEnabled: true,
- cachingEnabled: true,
- minifyAssets: true,
- enableCDN: process.env.ENABLE_CDN === 'true',
- healthEndpoints: true,
- metricsCollection: true
- },
-
- // Service dependencies
- dependencies: {
- required: ['database'],
- optional: ['redis', 'frontend'],
- order: ['database', 'redis', 'backend', 'frontend', 'healthMonitoring']
- },
-
- // Error handling
- errorHandling: {
- retryFailedServices: true,
- continueOnOptionalFailure: true,
- detailedErrorMessages: process.env.NODE_ENV === 'development',
- errorNotifications: process.env.ERROR_NOTIFICATIONS === 'true',
- crashReporting: process.env.CRASH_REPORTING === 'true'
- },
-
- // Paths and directories
- paths: {
- root: process.cwd(),
- src: path.join(process.cwd(), 'src'),
- config: path.join(process.cwd(), 'config'),
- logs: path.join(process.cwd(), 'logs'),
- scripts: path.join(process.cwd(), 'scripts'),
- frontend: path.join(process.cwd(), 'frontend'),
- database: path.join(process.cwd(), 'src', 'database'),
- migrations: path.join(process.cwd(), 'src', 'database', 'migrations'),
- seeds: path.join(process.cwd(), 'src', 'database', 'seeds')
- }
-};
-
-/**
- * Environment-specific configurations
- */
-const environmentConfigs = {
- development: {
- backend: {
- startupTimeout: 20000
- },
- frontend: {
- startupTimeout: 30000
- },
- database: {
- integrityChecks: false,
- autoSeed: true
- },
- healthMonitoring: {
- interval: 15000
- },
- logging: {
- level: 'debug'
- },
- startup: {
- verboseLogging: true,
- failFast: false
- }
- },
-
- production: {
- backend: {
- startupTimeout: 45000
- },
- frontend: {
- enabled: false // Assume pre-built assets are served by nginx/CDN
- },
- database: {
- integrityChecks: true,
- autoSeed: false,
- retryAttempts: 5
- },
- healthMonitoring: {
- interval: 60000,
- alertThresholds: {
- responseTime: 3000,
- memoryUsage: 85,
- cpuUsage: 85
- }
- },
- logging: {
- level: 'warn'
- },
- startup: {
- verboseLogging: false,
- failFast: true
- },
- errorHandling: {
- retryFailedServices: true,
- continueOnOptionalFailure: false
- }
- },
-
- staging: {
- backend: {
- startupTimeout: 30000
- },
- database: {
- integrityChecks: true,
- autoSeed: true
- },
- healthMonitoring: {
- interval: 30000
- },
- logging: {
- level: 'info'
- }
- },
-
- testing: {
- backend: {
- port: 0, // Use random available port
- startupTimeout: 10000
- },
- frontend: {
- enabled: false
- },
- database: {
- autoMigrate: true,
- autoSeed: true,
- integrityChecks: false
- },
- healthMonitoring: {
- enabled: false
- },
- preflightChecks: {
- enabled: false
- },
- startup: {
- enableBanner: false,
- verboseLogging: false
- }
- }
-};
-
-/**
- * Merge configurations based on environment
- */
-function mergeConfigs(base, override) {
- const result = { ...base };
-
- for (const [key, value] of Object.entries(override)) {
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
- result[key] = mergeConfigs(result[key] || {}, value);
- } else {
- result[key] = value;
- }
- }
-
- return result;
-}
-
-/**
- * Get configuration for current environment
- */
-function getConfig() {
- const environment = process.env.NODE_ENV || 'development';
- const envConfig = environmentConfigs[environment] || {};
-
- return mergeConfigs(defaultConfig, envConfig);
-}
-
-/**
- * Validate configuration
- */
-function validateConfig(config) {
- const errors = [];
-
- // Validate ports
- if (config.backend.port < 1 || config.backend.port > 65535) {
- errors.push(`Invalid backend port: ${config.backend.port}`);
- }
-
- if (config.frontend.enabled && (config.frontend.port < 1 || config.frontend.port > 65535)) {
- errors.push(`Invalid frontend port: ${config.frontend.port}`);
- }
-
- // Validate timeouts
- if (config.backend.startupTimeout < 1000) {
- errors.push('Backend startup timeout too low (minimum 1000ms)');
- }
-
- if (config.database.connectionTimeout < 1000) {
- errors.push('Database connection timeout too low (minimum 1000ms)');
- }
-
- // Validate required paths
- const requiredPaths = ['root', 'src', 'config'];
- for (const pathKey of requiredPaths) {
- if (!config.paths[pathKey]) {
- errors.push(`Missing required path: ${pathKey}`);
- }
- }
-
- if (errors.length > 0) {
- throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
- }
-
- return true;
-}
-
-/**
- * Export configuration
- */
-const config = getConfig();
-validateConfig(config);
-
-module.exports = {
- config,
- getConfig,
- validateConfig,
- defaultConfig,
- environmentConfigs
-};
\ No newline at end of file
diff --git a/frontend/.gitignore b/frontend/.gitignore
deleted file mode 100644
index a547bf3..0000000
--- a/frontend/.gitignore
+++ /dev/null
@@ -1,24 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
diff --git a/frontend/DEPLOYMENT.md b/frontend/DEPLOYMENT.md
deleted file mode 100644
index 6112bd3..0000000
--- a/frontend/DEPLOYMENT.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Frontend Deployment Notes
-
-## Node.js Version Compatibility
-
-The current setup uses Vite 7.x and React Router 7.x which require Node.js >= 20.0.0. The current environment is running Node.js 18.19.1.
-
-### Options to resolve:
-
-1. **Upgrade Node.js** (Recommended)
- ```bash
- # Update to Node.js 20 or later
- nvm install 20
- nvm use 20
- ```
-
-2. **Downgrade dependencies** (Alternative)
- ```bash
- npm install vite@^5.0.0 react-router-dom@^6.0.0
- ```
-
-## Production Build
-
-The build process works correctly despite version warnings:
-- TypeScript compilation: ✅ No errors
-- Bundle generation: ✅ Optimized chunks created
-- CSS processing: ✅ Tailwind compiled successfully
-
-## Development Server
-
-Due to Node.js version compatibility, the dev server may not start. This is resolved by upgrading Node.js or using the production build for testing.
-
-## Deployment Steps
-
-1. Ensure Node.js >= 20.0.0
-2. Install dependencies: `npm install`
-3. Build: `npm run build`
-4. Serve dist/ folder with any static file server
-
-## Integration with Backend
-
-The frontend is configured to connect to:
-- API: `http://localhost:3000`
-- WebSocket: `http://localhost:3000`
-
-Update `.env.development` or `.env.production` as needed for different environments.
-
-## Performance Optimizations
-
-- Code splitting by vendor, router, and UI libraries
-- Source maps for debugging
-- Gzip compression ready
-- Optimized dependency pre-bundling
-
-## Security Considerations
-
-- JWT tokens stored in localStorage (consider httpOnly cookies for production)
-- CORS configured for local development
-- Input validation on all forms
-- Protected routes with authentication guards
\ No newline at end of file
diff --git a/frontend/README.md b/frontend/README.md
deleted file mode 100644
index 7959ce4..0000000
--- a/frontend/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# React + TypeScript + Vite
-
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
-
-Currently, two official plugins are available:
-
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
-
-## Expanding the ESLint configuration
-
-If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
-
-```js
-export default tseslint.config([
- globalIgnores(['dist']),
- {
- files: ['**/*.{ts,tsx}'],
- extends: [
- // Other configs...
-
- // Remove tseslint.configs.recommended and replace with this
- ...tseslint.configs.recommendedTypeChecked,
- // Alternatively, use this for stricter rules
- ...tseslint.configs.strictTypeChecked,
- // Optionally, add this for stylistic rules
- ...tseslint.configs.stylisticTypeChecked,
-
- // Other configs...
- ],
- languageOptions: {
- parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
- tsconfigRootDir: import.meta.dirname,
- },
- // other options...
- },
- },
-])
-```
-
-You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
-
-```js
-// eslint.config.js
-import reactX from 'eslint-plugin-react-x'
-import reactDom from 'eslint-plugin-react-dom'
-
-export default tseslint.config([
- globalIgnores(['dist']),
- {
- files: ['**/*.{ts,tsx}'],
- extends: [
- // Other configs...
- // Enable lint rules for React
- reactX.configs['recommended-typescript'],
- // Enable lint rules for React DOM
- reactDom.configs.recommended,
- ],
- languageOptions: {
- parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
- tsconfigRootDir: import.meta.dirname,
- },
- // other options...
- },
- },
-])
-```
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js
deleted file mode 100644
index d94e7de..0000000
--- a/frontend/eslint.config.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import tseslint from 'typescript-eslint'
-import { globalIgnores } from 'eslint/config'
-
-export default tseslint.config([
- globalIgnores(['dist']),
- {
- files: ['**/*.{ts,tsx}'],
- extends: [
- js.configs.recommended,
- tseslint.configs.recommended,
- reactHooks.configs['recommended-latest'],
- reactRefresh.configs.vite,
- ],
- languageOptions: {
- ecmaVersion: 2020,
- globals: globals.browser,
- },
- },
-])
diff --git a/frontend/index.html b/frontend/index.html
deleted file mode 100644
index e4b78ea..0000000
--- a/frontend/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Vite + React + TS
-
-
-
-
-
-
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
deleted file mode 100644
index d99f34a..0000000
--- a/frontend/package-lock.json
+++ /dev/null
@@ -1,4509 +0,0 @@
-{
- "name": "shattered-void-frontend",
- "version": "0.1.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "shattered-void-frontend",
- "version": "0.1.0",
- "dependencies": {
- "@headlessui/react": "^2.2.7",
- "@heroicons/react": "^2.2.0",
- "@tailwindcss/postcss": "^4.1.11",
- "autoprefixer": "^10.4.21",
- "axios": "^1.11.0",
- "postcss": "^8.5.6",
- "react": "^19.1.0",
- "react-dom": "^19.1.0",
- "react-hot-toast": "^2.5.2",
- "react-router-dom": "^7.7.1",
- "socket.io-client": "^4.8.1",
- "tailwindcss": "^4.1.11",
- "zustand": "^5.0.7"
- },
- "devDependencies": {
- "@eslint/js": "^9.30.1",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
- "@vitejs/plugin-react": "^4.6.0",
- "eslint": "^9.30.1",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.3.0",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.35.1",
- "vite": "^7.0.4"
- }
- },
- "node_modules/@alloc/quick-lru": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
- "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
- "js-tokens": "^4.0.0",
- "picocolors": "^1.1.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/compat-data": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
- "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
- "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
- "dev": true,
- "dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.0",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.27.3",
- "@babel/helpers": "^7.27.6",
- "@babel/parser": "^7.28.0",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.0",
- "@babel/types": "^7.28.0",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "node_modules/@babel/generator": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
- "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.28.0",
- "@babel/types": "^7.28.0",
- "@jridgewell/gen-mapping": "^0.3.12",
- "@jridgewell/trace-mapping": "^0.3.28",
- "jsesc": "^3.0.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.27.2",
- "@babel/helper-validator-option": "^7.27.1",
- "browserslist": "^4.24.0",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
- "dev": true,
- "dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.27.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
- "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.27.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "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==",
- "dev": true,
- "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==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helpers": {
- "version": "7.28.2",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz",
- "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2"
- },
- "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==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.28.0"
- },
- "bin": {
- "parser": "bin/babel-parser.js"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx-self": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
- "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx-source": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
- "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
- "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.0",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.0",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.0",
- "debug": "^4.3.1"
- },
- "engines": {
- "node": ">=6.9.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==",
- "dev": true,
- "dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
- "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
- "dev": true,
- "dependencies": {
- "eslint-visitor-keys": "^3.4.3"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
- }
- },
- "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
- "dev": true,
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
- }
- },
- "node_modules/@eslint/config-array": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
- "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
- "dev": true,
- "dependencies": {
- "@eslint/object-schema": "^2.1.6",
- "debug": "^4.3.1",
- "minimatch": "^3.1.2"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/config-helpers": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
- "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/core": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
- "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
- "dev": true,
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
- "dev": true,
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/js": {
- "version": "9.32.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz",
- "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- }
- },
- "node_modules/@eslint/object-schema": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/plugin-kit": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
- "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
- "dev": true,
- "dependencies": {
- "@eslint/core": "^0.15.1",
- "levn": "^0.4.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@floating-ui/core": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
- "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
- "dependencies": {
- "@floating-ui/utils": "^0.2.10"
- }
- },
- "node_modules/@floating-ui/dom": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz",
- "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==",
- "dependencies": {
- "@floating-ui/core": "^1.7.3",
- "@floating-ui/utils": "^0.2.10"
- }
- },
- "node_modules/@floating-ui/react": {
- "version": "0.26.28",
- "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
- "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
- "dependencies": {
- "@floating-ui/react-dom": "^2.1.2",
- "@floating-ui/utils": "^0.2.8",
- "tabbable": "^6.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
- "node_modules/@floating-ui/react-dom": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz",
- "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==",
- "dependencies": {
- "@floating-ui/dom": "^1.7.3"
- },
- "peerDependencies": {
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
- "node_modules/@floating-ui/utils": {
- "version": "0.2.10",
- "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
- "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
- },
- "node_modules/@headlessui/react": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.7.tgz",
- "integrity": "sha512-WKdTymY8Y49H8/gUc/lIyYK1M+/6dq0Iywh4zTZVAaiTDprRfioxSgD0wnXTQTBpjpGJuTL1NO/mqEvc//5SSg==",
- "dependencies": {
- "@floating-ui/react": "^0.26.16",
- "@react-aria/focus": "^3.20.2",
- "@react-aria/interactions": "^3.25.0",
- "@tanstack/react-virtual": "^3.13.9",
- "use-sync-external-store": "^1.5.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "react": "^18 || ^19 || ^19.0.0-rc",
- "react-dom": "^18 || ^19 || ^19.0.0-rc"
- }
- },
- "node_modules/@heroicons/react": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
- "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
- "peerDependencies": {
- "react": ">= 16 || ^19.0.0-rc"
- }
- },
- "node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
- "dev": true,
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node": {
- "version": "0.16.6",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
- "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
- "dev": true,
- "dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.3.0"
- },
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
- "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
- "dev": true,
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
- "engines": {
- "node": ">=12.22"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/retry": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
- "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@isaacs/fs-minipass": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
- "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
- "dependencies": {
- "minipass": "^7.0.4"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.12",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
- "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "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=="
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.29",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
- "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@react-aria/focus": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.0.tgz",
- "integrity": "sha512-7NEGtTPsBy52EZ/ToVKCu0HSelE3kq9qeis+2eEq90XSuJOMaDHUQrA7RC2Y89tlEwQB31bud/kKRi9Qme1dkA==",
- "dependencies": {
- "@react-aria/interactions": "^3.25.4",
- "@react-aria/utils": "^3.30.0",
- "@react-types/shared": "^3.31.0",
- "@swc/helpers": "^0.5.0",
- "clsx": "^2.0.0"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
- "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
- }
- },
- "node_modules/@react-aria/interactions": {
- "version": "3.25.4",
- "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.4.tgz",
- "integrity": "sha512-HBQMxgUPHrW8V63u9uGgBymkMfj6vdWbB0GgUJY49K9mBKMsypcHeWkWM6+bF7kxRO728/IK8bWDV6whDbqjHg==",
- "dependencies": {
- "@react-aria/ssr": "^3.9.10",
- "@react-aria/utils": "^3.30.0",
- "@react-stately/flags": "^3.1.2",
- "@react-types/shared": "^3.31.0",
- "@swc/helpers": "^0.5.0"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
- "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
- }
- },
- "node_modules/@react-aria/ssr": {
- "version": "3.9.10",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
- "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
- "dependencies": {
- "@swc/helpers": "^0.5.0"
- },
- "engines": {
- "node": ">= 12"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
- }
- },
- "node_modules/@react-aria/utils": {
- "version": "3.30.0",
- "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.30.0.tgz",
- "integrity": "sha512-ydA6y5G1+gbem3Va2nczj/0G0W7/jUVo/cbN10WA5IizzWIwMP5qhFr7macgbKfHMkZ+YZC3oXnt2NNre5odKw==",
- "dependencies": {
- "@react-aria/ssr": "^3.9.10",
- "@react-stately/flags": "^3.1.2",
- "@react-stately/utils": "^3.10.8",
- "@react-types/shared": "^3.31.0",
- "@swc/helpers": "^0.5.0",
- "clsx": "^2.0.0"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
- "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
- }
- },
- "node_modules/@react-stately/flags": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
- "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
- "dependencies": {
- "@swc/helpers": "^0.5.0"
- }
- },
- "node_modules/@react-stately/utils": {
- "version": "3.10.8",
- "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
- "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
- "dependencies": {
- "@swc/helpers": "^0.5.0"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
- }
- },
- "node_modules/@react-types/shared": {
- "version": "3.31.0",
- "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.31.0.tgz",
- "integrity": "sha512-ua5U6V66gDcbLZe4P2QeyNgPp4YWD1ymGA6j3n+s8CGExtrCPe64v+g4mvpT8Bnb985R96e4zFT61+m0YCwqMg==",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
- }
- },
- "node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.27",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
- "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
- "dev": true
- },
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "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,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@socket.io/component-emitter": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
- "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
- },
- "node_modules/@swc/helpers": {
- "version": "0.5.17",
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
- "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
- "dependencies": {
- "tslib": "^2.8.0"
- }
- },
- "node_modules/@tailwindcss/node": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz",
- "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==",
- "dependencies": {
- "@ampproject/remapping": "^2.3.0",
- "enhanced-resolve": "^5.18.1",
- "jiti": "^2.4.2",
- "lightningcss": "1.30.1",
- "magic-string": "^0.30.17",
- "source-map-js": "^1.2.1",
- "tailwindcss": "4.1.11"
- }
- },
- "node_modules/@tailwindcss/oxide": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz",
- "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==",
- "hasInstallScript": true,
- "dependencies": {
- "detect-libc": "^2.0.4",
- "tar": "^7.4.3"
- },
- "engines": {
- "node": ">= 10"
- },
- "optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.1.11",
- "@tailwindcss/oxide-darwin-arm64": "4.1.11",
- "@tailwindcss/oxide-darwin-x64": "4.1.11",
- "@tailwindcss/oxide-freebsd-x64": "4.1.11",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11",
- "@tailwindcss/oxide-linux-arm64-musl": "4.1.11",
- "@tailwindcss/oxide-linux-x64-gnu": "4.1.11",
- "@tailwindcss/oxide-linux-x64-musl": "4.1.11",
- "@tailwindcss/oxide-wasm32-wasi": "4.1.11",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11",
- "@tailwindcss/oxide-win32-x64-msvc": "4.1.11"
- }
- },
- "node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz",
- "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz",
- "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz",
- "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz",
- "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz",
- "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==",
- "cpu": [
- "arm"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz",
- "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz",
- "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz",
- "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz",
- "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz",
- "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==",
- "bundleDependencies": [
- "@napi-rs/wasm-runtime",
- "@emnapi/core",
- "@emnapi/runtime",
- "@tybys/wasm-util",
- "@emnapi/wasi-threads",
- "tslib"
- ],
- "cpu": [
- "wasm32"
- ],
- "optional": true,
- "dependencies": {
- "@emnapi/core": "^1.4.3",
- "@emnapi/runtime": "^1.4.3",
- "@emnapi/wasi-threads": "^1.0.2",
- "@napi-rs/wasm-runtime": "^0.2.11",
- "@tybys/wasm-util": "^0.9.0",
- "tslib": "^2.8.0"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz",
- "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz",
- "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@tailwindcss/postcss": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz",
- "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==",
- "dependencies": {
- "@alloc/quick-lru": "^5.2.0",
- "@tailwindcss/node": "4.1.11",
- "@tailwindcss/oxide": "4.1.11",
- "postcss": "^8.4.41",
- "tailwindcss": "4.1.11"
- }
- },
- "node_modules/@tanstack/react-virtual": {
- "version": "3.13.12",
- "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz",
- "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==",
- "dependencies": {
- "@tanstack/virtual-core": "3.13.12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
- "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
- }
- },
- "node_modules/@tanstack/virtual-core": {
- "version": "3.13.12",
- "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
- "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@types/babel__core": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
- "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
- },
- "node_modules/@types/babel__generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
- "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__template": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
- "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
- "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.28.2"
- }
- },
- "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
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true
- },
- "node_modules/@types/react": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
- "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==",
- "devOptional": true,
- "dependencies": {
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@types/react-dom": {
- "version": "19.1.7",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
- "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
- "dev": true,
- "peerDependencies": {
- "@types/react": "^19.0.0"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
- "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
- "dev": true,
- "dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.38.0",
- "@typescript-eslint/type-utils": "8.38.0",
- "@typescript-eslint/utils": "8.38.0",
- "@typescript-eslint/visitor-keys": "8.38.0",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.38.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
- "dev": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/@typescript-eslint/parser": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
- "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.38.0",
- "@typescript-eslint/types": "8.38.0",
- "@typescript-eslint/typescript-estree": "8.38.0",
- "@typescript-eslint/visitor-keys": "8.38.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
- "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.38.0",
- "@typescript-eslint/types": "^8.38.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
- "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/types": "8.38.0",
- "@typescript-eslint/visitor-keys": "8.38.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
- "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
- "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/types": "8.38.0",
- "@typescript-eslint/typescript-estree": "8.38.0",
- "@typescript-eslint/utils": "8.38.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
- "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
- "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/project-service": "8.38.0",
- "@typescript-eslint/tsconfig-utils": "8.38.0",
- "@typescript-eslint/types": "8.38.0",
- "@typescript-eslint/visitor-keys": "8.38.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
- "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
- "dev": true,
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.38.0",
- "@typescript-eslint/types": "8.38.0",
- "@typescript-eslint/typescript-estree": "8.38.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
- "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/types": "8.38.0",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@vitejs/plugin-react": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
- "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.28.0",
- "@babel/plugin-transform-react-jsx-self": "^7.27.1",
- "@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.27",
- "@types/babel__core": "^7.20.5",
- "react-refresh": "^0.17.0"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "peerDependencies": {
- "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
- }
- },
- "node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
- "peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "node_modules/autoprefixer": {
- "version": "10.4.21",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
- "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "browserslist": "^4.24.4",
- "caniuse-lite": "^1.0.30001702",
- "fraction.js": "^4.3.7",
- "normalize-range": "^0.1.2",
- "picocolors": "^1.1.1",
- "postcss-value-parser": "^4.2.0"
- },
- "bin": {
- "autoprefixer": "bin/autoprefixer"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- },
- "peerDependencies": {
- "postcss": "^8.1.0"
- }
- },
- "node_modules/axios": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
- "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
- "dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.4",
- "proxy-from-env": "^1.1.0"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
- },
- "node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
- "dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browserslist": {
- "version": "4.25.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
- "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "caniuse-lite": "^1.0.30001726",
- "electron-to-chromium": "^1.5.173",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.3"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/call-bind-apply-helpers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001731",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz",
- "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ]
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/chownr": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
- "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true
- },
- "node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true
- },
- "node_modules/cookie": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
- "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
- },
- "node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
- "dev": true,
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true
- },
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "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==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/dunder-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "es-errors": "^1.3.0",
- "gopd": "^1.2.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.194",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz",
- "integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA=="
- },
- "node_modules/engine.io-client": {
- "version": "6.6.3",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
- "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.3.1",
- "engine.io-parser": "~5.2.1",
- "ws": "~8.17.1",
- "xmlhttprequest-ssl": "~2.1.1"
- }
- },
- "node_modules/engine.io-client/node_modules/debug": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
- "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/engine.io-parser": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
- "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/enhanced-resolve": {
- "version": "5.18.2",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz",
- "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==",
- "dependencies": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dependencies": {
- "es-errors": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-set-tostringtag": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
- "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
- "dependencies": {
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.6",
- "has-tostringtag": "^1.0.2",
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "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,
- "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/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint": {
- "version": "9.32.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
- "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
- "dev": true,
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.0",
- "@eslint/core": "^0.15.0",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.32.0",
- "@eslint/plugin-kit": "^0.3.4",
- "@humanfs/node": "^0.16.6",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.2",
- "@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.6",
- "debug": "^4.3.2",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.0",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^8.0.0",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://eslint.org/donate"
- },
- "peerDependencies": {
- "jiti": "*"
- },
- "peerDependenciesMeta": {
- "jiti": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
- }
- },
- "node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.20",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
- "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
- "dev": true,
- "peerDependencies": {
- "eslint": ">=8.40"
- }
- },
- "node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
- "dev": true,
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
- "dependencies": {
- "acorn": "^8.15.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
- "dev": true,
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "dependencies": {
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
- },
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
- },
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true
- },
- "node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
- "dev": true,
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/file-entry-cache": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
- "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
- "dev": true,
- "dependencies": {
- "flat-cache": "^4.0.0"
- },
- "engines": {
- "node": ">=16.0.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat-cache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
- "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
- "dev": true,
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
- "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
- "dev": true
- },
- "node_modules/follow-redirects": {
- "version": "1.15.11",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
- "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
- "node_modules/form-data": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
- "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "es-set-tostringtag": "^2.1.0",
- "hasown": "^2.0.2",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fraction.js": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
- "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
- "engines": {
- "node": "*"
- },
- "funding": {
- "type": "patreon",
- "url": "https://github.com/sponsors/rawify"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/get-intrinsic": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
- "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",
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "dependencies": {
- "dunder-proto": "^1.0.1",
- "es-object-atoms": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/globals": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
- "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
- "dev": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/goober": {
- "version": "2.1.16",
- "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
- "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
- "peerDependencies": {
- "csstype": "^3.0.10"
- }
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
- },
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/has-symbols": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-tostringtag": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
- "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dependencies": {
- "has-symbols": "^1.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/ignore": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
- "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
- "dev": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
- },
- "node_modules/jiti": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
- "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
- "bin": {
- "jiti": "lib/jiti-cli.mjs"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true
- },
- "node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
- },
- "node_modules/json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true
- },
- "node_modules/json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true,
- "bin": {
- "json5": "lib/cli.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/lightningcss": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
- "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
- "dependencies": {
- "detect-libc": "^2.0.3"
- },
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- },
- "optionalDependencies": {
- "lightningcss-darwin-arm64": "1.30.1",
- "lightningcss-darwin-x64": "1.30.1",
- "lightningcss-freebsd-x64": "1.30.1",
- "lightningcss-linux-arm-gnueabihf": "1.30.1",
- "lightningcss-linux-arm64-gnu": "1.30.1",
- "lightningcss-linux-arm64-musl": "1.30.1",
- "lightningcss-linux-x64-gnu": "1.30.1",
- "lightningcss-linux-x64-musl": "1.30.1",
- "lightningcss-win32-arm64-msvc": "1.30.1",
- "lightningcss-win32-x64-msvc": "1.30.1"
- }
- },
- "node_modules/lightningcss-darwin-arm64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
- "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-x64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
- "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-freebsd-x64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
- "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
- "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
- "cpu": [
- "arm"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
- "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
- "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
- "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-musl": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
- "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
- "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
- "cpu": [
- "arm64"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
- "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
- "cpu": [
- "x64"
- ],
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true
- },
- "node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "dependencies": {
- "yallist": "^3.0.2"
- }
- },
- "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==",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0"
- }
- },
- "node_modules/math-intrinsics": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
- "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
- "dev": true,
- "dependencies": {
- "braces": "^3.0.3",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/minizlib": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
- "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/mkdirp": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
- "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
- "bin": {
- "mkdirp": "dist/cjs/src/bin.js"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
- "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"
- }
- ],
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
- },
- "node_modules/node-releases": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="
- },
- "node_modules/normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
- "dev": true,
- "dependencies": {
- "deep-is": "^0.1.3",
- "fast-levenshtein": "^2.0.6",
- "levn": "^0.4.1",
- "prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.5"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "engines": {
- "node": ">=8.6"
- },
- "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"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.11",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
- },
- "node_modules/prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
- "dependencies": {
- "scheduler": "^0.26.0"
- },
- "peerDependencies": {
- "react": "^19.1.1"
- }
- },
- "node_modules/react-hot-toast": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz",
- "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==",
- "dependencies": {
- "csstype": "^3.1.3",
- "goober": "^2.1.16"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "react": ">=16",
- "react-dom": ">=16"
- }
- },
- "node_modules/react-refresh": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-router": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.1.tgz",
- "integrity": "sha512-jVKHXoWRIsD/qS6lvGveckwb862EekvapdHJN/cGmzw40KnJH5gg53ujOJ4qX6EKIK9LSBfFed/xiQ5yeXNrUA==",
- "dependencies": {
- "cookie": "^1.0.1",
- "set-cookie-parser": "^2.6.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/react-router-dom": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.1.tgz",
- "integrity": "sha512-bavdk2BA5r3MYalGKZ01u8PGuDBloQmzpBZVhDLrOOv1N943Wq6dcM9GhB3x8b7AbqPMEezauv4PeGkAJfy7FQ==",
- "dependencies": {
- "react-router": "7.7.1"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/reusify": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "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,
- "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/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
- "node_modules/scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
- },
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/set-cookie-parser": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
- "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/socket.io-client": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
- "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.3.2",
- "engine.io-client": "~6.6.1",
- "socket.io-parser": "~4.2.4"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/socket.io-client/node_modules/debug": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
- "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/socket.io-parser": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
- "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
- "dependencies": {
- "@socket.io/component-emitter": "~3.1.0",
- "debug": "~4.3.1"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/socket.io-parser/node_modules/debug": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
- "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/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==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/tabbable": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
- "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
- },
- "node_modules/tailwindcss": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
- "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="
- },
- "node_modules/tapable": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
- "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/tar": {
- "version": "7.4.3",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
- "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
- "dependencies": {
- "@isaacs/fs-minipass": "^4.0.0",
- "chownr": "^3.0.0",
- "minipass": "^7.1.2",
- "minizlib": "^3.0.1",
- "mkdirp": "^3.0.1",
- "yallist": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/tar/node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
- "engines": {
- "node": ">=18"
- }
- },
- "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,
- "dependencies": {
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/SuperchupuDev"
- }
- },
- "node_modules/tinyglobby/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,
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
- "dev": true,
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
- "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=="
- },
- "node_modules/type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/typescript-eslint": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz",
- "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/eslint-plugin": "8.38.0",
- "@typescript-eslint/parser": "8.38.0",
- "@typescript-eslint/typescript-estree": "8.38.0",
- "@typescript-eslint/utils": "8.38.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "escalade": "^3.2.0",
- "picocolors": "^1.1.1"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/use-sync-external-store": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
- "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
- }
- },
- "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,
- "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/vite/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,
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/vite/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/word-wrap": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ws": {
- "version": "8.17.1",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
- "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
- "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/xmlhttprequest-ssl": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
- "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/zustand": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.7.tgz",
- "integrity": "sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==",
- "engines": {
- "node": ">=12.20.0"
- },
- "peerDependencies": {
- "@types/react": ">=18.0.0",
- "immer": ">=9.0.6",
- "react": ">=18.0.0",
- "use-sync-external-store": ">=1.2.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "immer": {
- "optional": true
- },
- "react": {
- "optional": true
- },
- "use-sync-external-store": {
- "optional": true
- }
- }
- }
- }
-}
diff --git a/frontend/package.json b/frontend/package.json
deleted file mode 100644
index ade4346..0000000
--- a/frontend/package.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
- "name": "shattered-void-frontend",
- "private": true,
- "version": "0.1.0",
- "type": "module",
- "description": "Frontend for Shattered Void MMO - A post-collapse galaxy strategy game",
- "scripts": {
- "dev": "vite --port 5173 --host",
- "build": "tsc -b && vite build",
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
- "lint:fix": "eslint . --ext ts,tsx --fix",
- "preview": "vite preview --port 4173",
- "type-check": "tsc --noEmit",
- "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
- "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\""
- },
- "dependencies": {
- "@headlessui/react": "^2.2.7",
- "@heroicons/react": "^2.2.0",
- "@tailwindcss/postcss": "^4.1.11",
- "autoprefixer": "^10.4.21",
- "axios": "^1.11.0",
- "postcss": "^8.5.6",
- "react": "^19.1.0",
- "react-dom": "^19.1.0",
- "react-hot-toast": "^2.5.2",
- "react-router-dom": "^7.7.1",
- "socket.io-client": "^4.8.1",
- "tailwindcss": "^4.1.11",
- "zustand": "^5.0.7"
- },
- "devDependencies": {
- "@eslint/js": "^9.30.1",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
- "@vitejs/plugin-react": "^4.6.0",
- "eslint": "^9.30.1",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.3.0",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.35.1",
- "vite": "^7.0.4"
- }
-}
diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js
deleted file mode 100644
index bfff3c7..0000000
--- a/frontend/postcss.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import postcss from '@tailwindcss/postcss';
-
-export default {
- plugins: [
- postcss(),
- ],
-}
\ No newline at end of file
diff --git a/frontend/src/App.css b/frontend/src/App.css
deleted file mode 100644
index b9d355d..0000000
--- a/frontend/src/App.css
+++ /dev/null
@@ -1,42 +0,0 @@
-#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
-}
-
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
-}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
-}
-
-@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
-
-@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
-}
-
-.card {
- padding: 2em;
-}
-
-.read-the-docs {
- color: #888;
-}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
deleted file mode 100644
index 8b0db34..0000000
--- a/frontend/src/App.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-import React from 'react';
-import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
-import { Toaster } from 'react-hot-toast';
-
-// Layout components
-import Layout from './components/layout/Layout';
-import ProtectedRoute from './components/auth/ProtectedRoute';
-
-// Auth components
-import SimpleLoginForm from './components/auth/SimpleLoginForm';
-import SimpleRegisterForm from './components/auth/SimpleRegisterForm';
-
-// Page components
-import Dashboard from './pages/Dashboard';
-import Colonies from './pages/Colonies';
-
-// Import styles
-import './index.css';
-
-const App: React.FC = () => {
- return (
-
-
- {/* Toast notifications - available on all pages */}
-
-
-
- {/* Public routes (redirect to dashboard if authenticated) */}
-
-
-
- }
- />
-
-
-
- }
- />
-
- {/* Protected routes */}
-
-
-
- }
- >
- {/* Redirect root to dashboard */}
- } />
-
- {/* Main application routes */}
- } />
- } />
-
- {/* Placeholder routes for future implementation */}
-
- Fleet Management
- Coming soon...
-
- }
- />
-
- Research Laboratory
- Coming soon...
-
- }
- />
-
- Galaxy Map
- Coming soon...
-
- }
- />
-
- Player Profile
- Coming soon...
-
- }
- />
-
-
- {/* Catch-all route for 404 */}
-
-
-
- }
- />
-
-
-
- );
-};
-
-export default App;
\ No newline at end of file
diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg
deleted file mode 100644
index 6c87de9..0000000
--- a/frontend/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/components/auth/LoginForm.tsx b/frontend/src/components/auth/LoginForm.tsx
deleted file mode 100644
index d12ce8e..0000000
--- a/frontend/src/components/auth/LoginForm.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import React, { useState } from 'react';
-import { Link, Navigate } from 'react-router-dom';
-import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
-import { useAuthStore } from '../../store/authStore';
-import type { LoginCredentials } from '../../types';
-
-const LoginForm: React.FC = () => {
- const [credentials, setCredentials] = useState({
- email: '',
- password: '',
- });
- const [showPassword, setShowPassword] = useState(false);
- const [validationErrors, setValidationErrors] = useState>({});
-
- const { login, isLoading, isAuthenticated } = useAuthStore();
-
- // Redirect if already authenticated
- if (isAuthenticated) {
- return ;
- }
-
- const validateForm = (): boolean => {
- const errors: Record = {};
-
- if (!credentials.email) {
- errors.email = 'Email is required';
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(credentials.email)) {
- errors.email = 'Please enter a valid email';
- }
-
- if (!credentials.password) {
- errors.password = 'Password is required';
- } else if (credentials.password.length < 6) {
- errors.password = 'Password must be at least 6 characters';
- }
-
- setValidationErrors(errors);
- return Object.keys(errors).length === 0;
- };
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
-
- if (!validateForm()) {
- return;
- }
-
- const success = await login(credentials);
- if (success) {
- // Navigation will be handled by the store/auth guard
- }
- };
-
- const handleInputChange = (field: keyof LoginCredentials, value: string) => {
- setCredentials(prev => ({ ...prev, [field]: value }));
-
- // Clear validation error when user starts typing
- if (validationErrors[field]) {
- setValidationErrors(prev => ({ ...prev, [field]: '' }));
- }
- };
-
- return (
-
-
-
-
- Sign in to Shattered Void
-
-
- Or{' '}
-
- create a new account
-
-
-
-
-
-
-
- );
-};
-
-export default LoginForm;
\ No newline at end of file
diff --git a/frontend/src/components/auth/ProtectedRoute.tsx b/frontend/src/components/auth/ProtectedRoute.tsx
deleted file mode 100644
index d095d8f..0000000
--- a/frontend/src/components/auth/ProtectedRoute.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react';
-import { Navigate, useLocation } from 'react-router-dom';
-import { useAuthStore } from '../../store/authStore';
-
-interface ProtectedRouteProps {
- children: React.ReactNode;
- requireAuth?: boolean;
-}
-
-const ProtectedRoute: React.FC = ({
- children,
- requireAuth = true
-}) => {
- const { isAuthenticated, isLoading } = useAuthStore();
- const location = useLocation();
-
- // Show loading spinner while checking authentication
- if (isLoading) {
- return (
-
- );
- }
-
- // If route requires authentication and user is not authenticated
- if (requireAuth && !isAuthenticated) {
- // Save the attempted location for redirecting after login
- return ;
- }
-
- // If route is for non-authenticated users (like login/register) and user is authenticated
- if (!requireAuth && isAuthenticated) {
- // Redirect to dashboard or the intended location
- const from = location.state?.from?.pathname || '/dashboard';
- return ;
- }
-
- // Render the protected content
- return <>{children}>;
-};
-
-export default ProtectedRoute;
\ No newline at end of file
diff --git a/frontend/src/components/auth/RegisterForm.tsx b/frontend/src/components/auth/RegisterForm.tsx
deleted file mode 100644
index 9b81b1e..0000000
--- a/frontend/src/components/auth/RegisterForm.tsx
+++ /dev/null
@@ -1,293 +0,0 @@
-import React, { useState } from 'react';
-import { Link, Navigate } from 'react-router-dom';
-import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
-import { useAuthStore } from '../../store/authStore';
-import type { RegisterCredentials } from '../../types';
-
-const RegisterForm: React.FC = () => {
- const [credentials, setCredentials] = useState({
- username: '',
- email: '',
- password: '',
- confirmPassword: '',
- });
- const [showPassword, setShowPassword] = useState(false);
- const [showConfirmPassword, setShowConfirmPassword] = useState(false);
- const [validationErrors, setValidationErrors] = useState>({});
-
- const { register, isLoading, isAuthenticated } = useAuthStore();
-
- // Redirect if already authenticated
- if (isAuthenticated) {
- return ;
- }
-
- const validateForm = (): boolean => {
- const errors: Record = {};
-
- if (!credentials.username) {
- errors.username = 'Username is required';
- } else if (credentials.username.length < 3) {
- errors.username = 'Username must be at least 3 characters';
- } else if (credentials.username.length > 20) {
- errors.username = 'Username must be less than 20 characters';
- } else if (!/^[a-zA-Z0-9_]+$/.test(credentials.username)) {
- errors.username = 'Username can only contain letters, numbers, and underscores';
- }
-
- if (!credentials.email) {
- errors.email = 'Email is required';
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(credentials.email)) {
- errors.email = 'Please enter a valid email';
- }
-
- if (!credentials.password) {
- errors.password = 'Password is required';
- } else if (credentials.password.length < 8) {
- errors.password = 'Password must be at least 8 characters';
- } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(credentials.password)) {
- errors.password = 'Password must contain at least one uppercase letter, one lowercase letter, and one number';
- }
-
- if (!credentials.confirmPassword) {
- errors.confirmPassword = 'Please confirm your password';
- } else if (credentials.password !== credentials.confirmPassword) {
- errors.confirmPassword = 'Passwords do not match';
- }
-
- setValidationErrors(errors);
- return Object.keys(errors).length === 0;
- };
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
-
- if (!validateForm()) {
- return;
- }
-
- const success = await register(credentials);
- if (success) {
- // Navigation will be handled by the store/auth guard
- }
- };
-
- const handleInputChange = (field: keyof RegisterCredentials, value: string) => {
- setCredentials(prev => ({ ...prev, [field]: value }));
-
- // Clear validation error when user starts typing
- if (validationErrors[field]) {
- setValidationErrors(prev => ({ ...prev, [field]: '' }));
- }
- };
-
- const getPasswordStrength = (password: string): { score: number; text: string; color: string } => {
- let score = 0;
-
- if (password.length >= 8) score++;
- if (/[a-z]/.test(password)) score++;
- if (/[A-Z]/.test(password)) score++;
- if (/\d/.test(password)) score++;
- if (/[^a-zA-Z\d]/.test(password)) score++;
-
- const strength = {
- 0: { text: 'Very Weak', color: 'bg-red-500' },
- 1: { text: 'Weak', color: 'bg-red-400' },
- 2: { text: 'Fair', color: 'bg-yellow-500' },
- 3: { text: 'Good', color: 'bg-yellow-400' },
- 4: { text: 'Strong', color: 'bg-green-500' },
- 5: { text: 'Very Strong', color: 'bg-green-600' },
- };
-
- return { score, ...strength[Math.min(score, 5) as keyof typeof strength] };
- };
-
- const passwordStrength = getPasswordStrength(credentials.password);
-
- return (
-
-
-
-
- Join Shattered Void
-
-
- Or{' '}
-
- sign in to your existing account
-
-
-
-
-
-
-
- );
-};
-
-export default RegisterForm;
\ No newline at end of file
diff --git a/frontend/src/components/auth/SimpleLoginForm.tsx b/frontend/src/components/auth/SimpleLoginForm.tsx
deleted file mode 100644
index eedfe46..0000000
--- a/frontend/src/components/auth/SimpleLoginForm.tsx
+++ /dev/null
@@ -1,272 +0,0 @@
-import React, { useState } from 'react';
-import { Link, Navigate } from 'react-router-dom';
-import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
-import { useAuthStore } from '../../store/authStore';
-import toast from 'react-hot-toast';
-
-interface LoginCredentials {
- email: string;
- password: string;
- rememberMe?: boolean;
-}
-
-const SimpleLoginForm: React.FC = () => {
- const [credentials, setCredentials] = useState({
- email: '',
- password: '',
- rememberMe: false,
- });
- const [showPassword, setShowPassword] = useState(false);
- const [validationErrors, setValidationErrors] = useState>({});
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- const { isAuthenticated } = useAuthStore();
-
- // Redirect if already authenticated
- if (isAuthenticated) {
- return ;
- }
-
- const validateForm = (): boolean => {
- const errors: Record = {};
-
- // Email validation
- if (!credentials.email.trim()) {
- errors.email = 'Email is required';
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(credentials.email)) {
- errors.email = 'Please enter a valid email';
- }
-
- // Password validation
- if (!credentials.password) {
- errors.password = 'Password is required';
- }
-
- setValidationErrors(errors);
- return Object.keys(errors).length === 0;
- };
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
-
- console.log('Login form submitted with:', { ...credentials, password: '[HIDDEN]' });
-
- if (!validateForm()) {
- toast.error('Please fix the validation errors');
- return;
- }
-
- setIsSubmitting(true);
-
- try {
- // Make direct API call
- const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3000';
- console.log('Making login request to:', `${apiUrl}/api/auth/login`);
-
- const response = await fetch(`${apiUrl}/api/auth/login`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify({
- email: credentials.email.trim().toLowerCase(),
- password: credentials.password,
- rememberMe: credentials.rememberMe,
- }),
- });
-
- console.log('Login response status:', response.status);
- console.log('Login response headers:', Object.fromEntries(response.headers.entries()));
-
- const data = await response.json();
- console.log('Login response data:', data);
-
- if (response.ok && data.success) {
- toast.success('Login successful! Welcome back!');
-
- // Store auth data manually
- if (data.data?.token && data.data?.user) {
- localStorage.setItem('accessToken', data.data.token);
- localStorage.setItem('user', JSON.stringify(data.data.user));
- }
-
- // Redirect to dashboard
- setTimeout(() => {
- window.location.href = '/dashboard';
- }, 1000);
- } else {
- console.error('Login failed:', data);
-
- if (data.errors && Array.isArray(data.errors)) {
- // Handle validation errors from backend
- const backendErrors: Record = {};
- data.errors.forEach((error: any) => {
- if (error.field && error.message) {
- backendErrors[error.field] = error.message;
- }
- });
- setValidationErrors(backendErrors);
- toast.error('Login failed. Please check the errors below.');
- } else {
- toast.error(data.message || 'Login failed. Please check your credentials.');
- }
- }
- } catch (error) {
- console.error('Network error during login:', error);
- toast.error('Network error. Please check your connection and try again.');
- } finally {
- setIsSubmitting(false);
- }
- };
-
- const handleInputChange = (field: keyof LoginCredentials, value: string | boolean) => {
- setCredentials(prev => ({ ...prev, [field]: value }));
-
- // Clear validation error when user starts typing
- if (typeof value === 'string' && validationErrors[field]) {
- setValidationErrors(prev => ({ ...prev, [field]: '' }));
- }
- };
-
- return (
-
-
-
-
- Welcome Back
-
-
- Sign in to your Shattered Void account
-
-
- Or{' '}
-
- create a new account
-
-
-
-
-
-
-
- );
-};
-
-export default SimpleLoginForm;
\ No newline at end of file
diff --git a/frontend/src/components/auth/SimpleRegisterForm.tsx b/frontend/src/components/auth/SimpleRegisterForm.tsx
deleted file mode 100644
index 4f59dd0..0000000
--- a/frontend/src/components/auth/SimpleRegisterForm.tsx
+++ /dev/null
@@ -1,335 +0,0 @@
-import React, { useState } from 'react';
-import { Link, Navigate } from 'react-router-dom';
-import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
-import { useAuthStore } from '../../store/authStore';
-import toast from 'react-hot-toast';
-
-interface RegisterCredentials {
- username: string;
- email: string;
- password: string;
- confirmPassword: string;
-}
-
-const SimpleRegisterForm: React.FC = () => {
- const [credentials, setCredentials] = useState({
- username: '',
- email: '',
- password: '',
- confirmPassword: '',
- });
- const [showPassword, setShowPassword] = useState(false);
- const [showConfirmPassword, setShowConfirmPassword] = useState(false);
- const [validationErrors, setValidationErrors] = useState>({});
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- const { isAuthenticated } = useAuthStore();
-
- // Redirect if already authenticated
- if (isAuthenticated) {
- return ;
- }
-
- const validateForm = (): boolean => {
- const errors: Record = {};
-
- // Username validation - simple
- if (!credentials.username.trim()) {
- errors.username = 'Username is required';
- } else if (credentials.username.length < 3) {
- errors.username = 'Username must be at least 3 characters';
- } else if (credentials.username.length > 30) {
- errors.username = 'Username must be less than 30 characters';
- } else if (!/^[a-zA-Z0-9_-]+$/.test(credentials.username)) {
- errors.username = 'Username can only contain letters, numbers, underscores, and hyphens';
- }
-
- // Email validation - simple
- if (!credentials.email.trim()) {
- errors.email = 'Email is required';
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(credentials.email)) {
- errors.email = 'Please enter a valid email';
- }
-
- // Password validation - simplified (6+ characters)
- if (!credentials.password) {
- errors.password = 'Password is required';
- } else if (credentials.password.length < 6) {
- errors.password = 'Password must be at least 6 characters';
- } else if (credentials.password.length > 128) {
- errors.password = 'Password must be less than 128 characters';
- }
-
- // Confirm password
- if (!credentials.confirmPassword) {
- errors.confirmPassword = 'Please confirm your password';
- } else if (credentials.password !== credentials.confirmPassword) {
- errors.confirmPassword = 'Passwords do not match';
- }
-
- setValidationErrors(errors);
- return Object.keys(errors).length === 0;
- };
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
-
- console.log('Form submitted with:', { ...credentials, password: '[HIDDEN]' });
-
- if (!validateForm()) {
- toast.error('Please fix the validation errors');
- return;
- }
-
- setIsSubmitting(true);
-
- try {
- // Make direct API call instead of using auth store
- const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3000';
- console.log('Making request to:', `${apiUrl}/api/auth/register`);
-
- const response = await fetch(`${apiUrl}/api/auth/register`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify({
- username: credentials.username.trim(),
- email: credentials.email.trim().toLowerCase(),
- password: credentials.password,
- }),
- });
-
- console.log('Response status:', response.status);
- console.log('Response headers:', Object.fromEntries(response.headers.entries()));
-
- const data = await response.json();
- console.log('Response data:', data);
-
- if (response.ok && data.success) {
- toast.success('Registration successful! Welcome to Shattered Void!');
-
- // Store auth data manually since we're bypassing the store
- if (data.data?.token && data.data?.user) {
- localStorage.setItem('accessToken', data.data.token);
- localStorage.setItem('user', JSON.stringify(data.data.user));
- }
-
- // Redirect to dashboard
- setTimeout(() => {
- window.location.href = '/dashboard';
- }, 1000);
- } else {
- console.error('Registration failed:', data);
-
- if (data.errors && Array.isArray(data.errors)) {
- // Handle validation errors from backend
- const backendErrors: Record = {};
- data.errors.forEach((error: any) => {
- if (error.field && error.message) {
- backendErrors[error.field] = error.message;
- }
- });
- setValidationErrors(backendErrors);
- toast.error('Registration failed. Please check the errors below.');
- } else {
- toast.error(data.message || 'Registration failed. Please try again.');
- }
- }
- } catch (error) {
- console.error('Network error during registration:', error);
- toast.error('Network error. Please check your connection and try again.');
- } finally {
- setIsSubmitting(false);
- }
- };
-
- const handleInputChange = (field: keyof RegisterCredentials, value: string) => {
- setCredentials(prev => ({ ...prev, [field]: value }));
-
- // Clear validation error when user starts typing
- if (validationErrors[field]) {
- setValidationErrors(prev => ({ ...prev, [field]: '' }));
- }
- };
-
- return (
-
-
-
-
- Join Shattered Void
-
-
- Create your account and start your galactic journey
-
-
- Or{' '}
-
- sign in to your existing account
-
-
-
-
-
-
-
- );
-};
-
-export default SimpleRegisterForm;
\ No newline at end of file
diff --git a/frontend/src/components/layout/Layout.tsx b/frontend/src/components/layout/Layout.tsx
deleted file mode 100644
index 8285d8f..0000000
--- a/frontend/src/components/layout/Layout.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react';
-import { Outlet } from 'react-router-dom';
-import Navigation from './Navigation';
-import { useWebSocket } from '../../hooks/useWebSocket';
-
-const Layout: React.FC = () => {
- // Initialize WebSocket connection for authenticated users
- const { isConnected, isConnecting } = useWebSocket();
-
- return (
-
-
-
- {/* Connection status indicator */}
-
-
-
-
-
-
- {isConnected
- ? 'Connected'
- : isConnecting
- ? 'Connecting...'
- : 'Disconnected'}
-
-
-
-
-
-
- {/* Main content */}
-
-
-
-
-
-
- );
-};
-
-export default Layout;
\ No newline at end of file
diff --git a/frontend/src/components/layout/Navigation.tsx b/frontend/src/components/layout/Navigation.tsx
deleted file mode 100644
index c7188eb..0000000
--- a/frontend/src/components/layout/Navigation.tsx
+++ /dev/null
@@ -1,252 +0,0 @@
-import React, { useState } from 'react';
-import { Link, useLocation } from 'react-router-dom';
-import { Disclosure } from '@headlessui/react';
-import {
- Bars3Icon,
- XMarkIcon,
- HomeIcon,
- BuildingOfficeIcon,
- RocketLaunchIcon,
- BeakerIcon,
- MapIcon,
- BellIcon,
- UserCircleIcon,
- ArrowRightOnRectangleIcon,
-} from '@heroicons/react/24/outline';
-import { useAuthStore } from '../../store/authStore';
-import { useGameStore } from '../../store/gameStore';
-import type { NavItem } from '../../types';
-
-const Navigation: React.FC = () => {
- const location = useLocation();
- const { user, logout } = useAuthStore();
- const { totalResources } = useGameStore();
- const [showUserMenu, setShowUserMenu] = useState(false);
-
- const navigation: NavItem[] = [
- { name: 'Dashboard', href: '/dashboard', icon: HomeIcon },
- { name: 'Colonies', href: '/colonies', icon: BuildingOfficeIcon },
- { name: 'Fleets', href: '/fleets', icon: RocketLaunchIcon },
- { name: 'Research', href: '/research', icon: BeakerIcon },
- { name: 'Galaxy', href: '/galaxy', icon: MapIcon },
- ];
-
- const isCurrentPath = (href: string) => {
- return location.pathname === href || location.pathname.startsWith(href + '/');
- };
-
- const handleLogout = () => {
- logout();
- setShowUserMenu(false);
- };
-
- return (
-
- {({ open }) => (
- <>
-
-
-
- {/* Logo */}
-
-
- Shattered Void
-
-
-
- {/* Desktop navigation */}
-
- {navigation.map((item) => {
- const Icon = item.icon;
- const current = isCurrentPath(item.href);
-
- return (
-
- {Icon && }
- {item.name}
-
- );
- })}
-
-
-
- {/* Resource display */}
- {totalResources && (
-
-
- Scrap:
-
- {totalResources.scrap.toLocaleString()}
-
-
-
- Energy:
-
- {totalResources.energy.toLocaleString()}
-
-
-
- Research:
-
- {totalResources.research_points.toLocaleString()}
-
-
-
- )}
-
- {/* User menu */}
-
-
-
- {/* Profile dropdown */}
-
-
-
-
-
- {showUserMenu && (
-
-
setShowUserMenu(false)}
- >
-
- Your Profile
-
-
-
- )}
-
-
-
- {/* Mobile menu button */}
-
-
- Open main menu
- {open ? (
-
- ) : (
-
- )}
-
-
-
-
-
- {/* Mobile menu */}
-
-
- {navigation.map((item) => {
- const Icon = item.icon;
- const current = isCurrentPath(item.href);
-
- return (
-
- {Icon && }
- {item.name}
-
- );
- })}
-
-
- {/* Mobile resources */}
- {totalResources && (
-
-
-
- Scrap:
-
- {totalResources.scrap.toLocaleString()}
-
-
-
- Energy:
-
- {totalResources.energy.toLocaleString()}
-
-
-
- Research:
-
- {totalResources.research_points.toLocaleString()}
-
-
-
-
- )}
-
- {/* Mobile user menu */}
-
-
-
-
-
-
-
{user?.username}
-
{user?.email}
-
-
-
-
-
- Your Profile
-
-
-
-
-
- >
- )}
-
- );
-};
-
-export default Navigation;
\ No newline at end of file
diff --git a/frontend/src/hooks/useWebSocket.ts b/frontend/src/hooks/useWebSocket.ts
deleted file mode 100644
index 5a4cbc8..0000000
--- a/frontend/src/hooks/useWebSocket.ts
+++ /dev/null
@@ -1,231 +0,0 @@
-import { useEffect, useRef, useState } from 'react';
-import { io, Socket } from 'socket.io-client';
-import { useAuthStore } from '../store/authStore';
-import { useGameStore } from '../store/gameStore';
-import type { GameEvent } from '../types';
-import toast from 'react-hot-toast';
-
-interface UseWebSocketOptions {
- autoConnect?: boolean;
- reconnectionAttempts?: number;
- reconnectionDelay?: number;
-}
-
-export const useWebSocket = (options: UseWebSocketOptions = {}) => {
- const {
- autoConnect = true,
- reconnectionAttempts = 5,
- reconnectionDelay = 1000,
- } = options;
-
- const socketRef = useRef(null);
- const reconnectTimeoutRef = useRef(null);
- const reconnectAttemptsRef = useRef(0);
-
- const [isConnected, setIsConnected] = useState(false);
- const [isConnecting, setIsConnecting] = useState(false);
-
- const { isAuthenticated, token } = useAuthStore();
- const { updateColony, updateFleet, updateResearch } = useGameStore();
-
- const connect = () => {
- if (socketRef.current?.connected || isConnecting || !isAuthenticated || !token) {
- return;
- }
-
- setIsConnecting(true);
-
- const wsUrl = import.meta.env.VITE_WS_URL || 'http://localhost:3000';
-
- socketRef.current = io(wsUrl, {
- auth: {
- token,
- },
- transports: ['websocket', 'polling'],
- timeout: 10000,
- reconnection: false, // We handle reconnection manually
- });
-
- const socket = socketRef.current;
-
- socket.on('connect', () => {
- console.log('WebSocket connected');
- setIsConnected(true);
- setIsConnecting(false);
- reconnectAttemptsRef.current = 0;
-
- // Clear any pending reconnection timeout
- if (reconnectTimeoutRef.current) {
- clearTimeout(reconnectTimeoutRef.current);
- reconnectTimeoutRef.current = null;
- }
- });
-
- socket.on('disconnect', (reason) => {
- console.log('WebSocket disconnected:', reason);
- setIsConnected(false);
- setIsConnecting(false);
-
- // Only attempt reconnection if it wasn't a manual disconnect
- if (reason !== 'io client disconnect' && isAuthenticated) {
- scheduleReconnect();
- }
- });
-
- socket.on('connect_error', (error) => {
- console.error('WebSocket connection error:', error);
- setIsConnected(false);
- setIsConnecting(false);
-
- if (isAuthenticated) {
- scheduleReconnect();
- }
- });
-
- // Game event handlers
- socket.on('game_event', (event: GameEvent) => {
- handleGameEvent(event);
- });
-
- socket.on('colony_update', (data) => {
- updateColony(data.colony_id, data.updates);
- });
-
- socket.on('fleet_update', (data) => {
- updateFleet(data.fleet_id, data.updates);
- });
-
- socket.on('research_complete', (data) => {
- updateResearch(data.research_id, {
- is_researching: false,
- level: data.new_level
- });
- toast.success(`Research completed: ${data.technology_name}`);
- });
-
- socket.on('building_complete', (data) => {
- updateColony(data.colony_id, {
- buildings: data.buildings
- });
- toast.success(`Building completed: ${data.building_name}`);
- });
-
- socket.on('resource_update', (data) => {
- updateColony(data.colony_id, {
- resources: data.resources
- });
- });
-
- // Error handling
- socket.on('error', (error) => {
- console.error('WebSocket error:', error);
- toast.error('Connection error occurred');
- });
- };
-
- const disconnect = () => {
- if (reconnectTimeoutRef.current) {
- clearTimeout(reconnectTimeoutRef.current);
- reconnectTimeoutRef.current = null;
- }
-
- if (socketRef.current) {
- socketRef.current.disconnect();
- socketRef.current = null;
- }
-
- setIsConnected(false);
- setIsConnecting(false);
- reconnectAttemptsRef.current = 0;
- };
-
- const scheduleReconnect = () => {
- if (reconnectAttemptsRef.current >= reconnectionAttempts) {
- console.log('Max reconnection attempts reached');
- toast.error('Connection lost. Please refresh the page.');
- return;
- }
-
- const delay = reconnectionDelay * Math.pow(2, reconnectAttemptsRef.current);
- console.log(`Scheduling reconnection attempt ${reconnectAttemptsRef.current + 1} in ${delay}ms`);
-
- reconnectTimeoutRef.current = setTimeout(() => {
- reconnectAttemptsRef.current++;
- connect();
- }, delay);
- };
-
- const handleGameEvent = (event: GameEvent) => {
- console.log('Game event received:', event);
-
- switch (event.type) {
- case 'colony_update':
- updateColony(event.data.colony_id, event.data.updates);
- break;
-
- case 'fleet_update':
- updateFleet(event.data.fleet_id, event.data.updates);
- break;
-
- case 'research_complete':
- updateResearch(event.data.research_id, {
- is_researching: false,
- level: event.data.new_level
- });
- toast.success(`Research completed: ${event.data.technology_name}`);
- break;
-
- case 'building_complete':
- updateColony(event.data.colony_id, {
- buildings: event.data.buildings
- });
- toast.success(`Building completed: ${event.data.building_name}`);
- break;
-
- case 'resource_update':
- updateColony(event.data.colony_id, {
- resources: event.data.resources
- });
- break;
-
- default:
- console.log('Unhandled game event type:', event.type);
- }
- };
-
- const sendMessage = (type: string, data: any) => {
- if (socketRef.current?.connected) {
- socketRef.current.emit(type, data);
- } else {
- console.warn('Cannot send message: WebSocket not connected');
- }
- };
-
- // Effect to handle connection lifecycle
- useEffect(() => {
- if (autoConnect && isAuthenticated && token) {
- connect();
- } else if (!isAuthenticated) {
- disconnect();
- }
-
- return () => {
- disconnect();
- };
- }, [isAuthenticated, token, autoConnect]);
-
- // Cleanup on unmount
- useEffect(() => {
- return () => {
- disconnect();
- };
- }, []);
-
- return {
- isConnected,
- isConnecting,
- connect,
- disconnect,
- sendMessage,
- };
-};
\ No newline at end of file
diff --git a/frontend/src/index.css b/frontend/src/index.css
deleted file mode 100644
index 59532a7..0000000
--- a/frontend/src/index.css
+++ /dev/null
@@ -1,67 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-/* Custom scrollbar styles */
-@layer utilities {
- .scrollbar-thin {
- scrollbar-width: thin;
- scrollbar-color: rgb(71 85 105) transparent;
- }
-
- .scrollbar-thin::-webkit-scrollbar {
- width: 6px;
- }
-
- .scrollbar-thin::-webkit-scrollbar-track {
- background: transparent;
- }
-
- .scrollbar-thin::-webkit-scrollbar-thumb {
- background-color: rgb(71 85 105);
- border-radius: 3px;
- }
-
- .scrollbar-thin::-webkit-scrollbar-thumb:hover {
- background-color: rgb(100 116 139);
- }
-}
-
-/* Game-specific styles */
-@layer components {
- .btn-primary {
- @apply bg-primary-600 hover:bg-primary-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2;
- }
-
- .btn-secondary {
- @apply bg-dark-700 hover:bg-dark-600 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-dark-500 focus:ring-offset-2;
- }
-
- .card {
- @apply bg-dark-800 border border-dark-700 rounded-lg p-6 shadow-lg;
- }
-
- .input-field {
- @apply w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-dark-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500;
- }
-
- .resource-display {
- @apply flex items-center space-x-2 px-3 py-2 bg-dark-700 rounded-lg border border-dark-600;
- }
-}
-
-/* Base styles */
-body {
- @apply bg-dark-900 text-white font-sans antialiased;
- margin: 0;
- min-height: 100vh;
-}
-
-/* Loading animations */
-.loading-pulse {
- @apply animate-pulse bg-dark-700 rounded;
-}
-
-.loading-spinner {
- @apply animate-spin rounded-full border-2 border-dark-600 border-t-primary-500;
-}
\ No newline at end of file
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
deleted file mode 100644
index db5b45e..0000000
--- a/frontend/src/lib/api.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import axios, { type AxiosResponse, AxiosError } from 'axios';
-import type { ApiResponse } from '../types';
-
-// Create axios instance with base configuration
-const api = axios.create({
- baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000',
- timeout: 10000,
- headers: {
- 'Content-Type': 'application/json',
- },
-});
-
-// Request interceptor to add auth token
-api.interceptors.request.use(
- (config) => {
- const token = localStorage.getItem('auth_token');
- if (token) {
- config.headers.Authorization = `Bearer ${token}`;
- }
- return config;
- },
- (error) => {
- return Promise.reject(error);
- }
-);
-
-// Response interceptor for error handling
-api.interceptors.response.use(
- (response: AxiosResponse) => {
- return response;
- },
- (error: AxiosError) => {
- // Handle token expiration
- if (error.response?.status === 401) {
- localStorage.removeItem('auth_token');
- localStorage.removeItem('user_data');
- window.location.href = '/login';
- }
-
- // Handle network errors
- if (!error.response) {
- console.error('Network error:', error.message);
- }
-
- return Promise.reject(error);
- }
-);
-
-// API methods
-export const apiClient = {
- // Authentication
- auth: {
- login: (credentials: { email: string; password: string }) =>
- api.post>('/api/auth/login', credentials),
-
- register: (userData: { username: string; email: string; password: string }) =>
- api.post>('/api/auth/register', userData),
-
- logout: () =>
- api.post>('/api/auth/logout'),
-
- forgotPassword: (email: string) =>
- api.post>('/api/auth/forgot-password', { email }),
-
- resetPassword: (token: string, password: string) =>
- api.post>('/api/auth/reset-password', { token, password }),
-
- verifyEmail: (token: string) =>
- api.post>('/api/auth/verify-email', { token }),
-
- refreshToken: () =>
- api.post>('/api/auth/refresh'),
- },
-
- // Player
- player: {
- getProfile: () =>
- api.get>('/api/player/profile'),
-
- updateProfile: (profileData: any) =>
- api.put>('/api/player/profile', profileData),
-
- getStats: () =>
- api.get>('/api/player/stats'),
- },
-
- // Colonies
- colonies: {
- getAll: () =>
- api.get>('/api/player/colonies'),
-
- getById: (id: number) =>
- api.get>(`/api/player/colonies/${id}`),
-
- create: (colonyData: { name: string; coordinates: string; planet_type_id: number }) =>
- api.post>('/api/player/colonies', colonyData),
-
- update: (id: number, colonyData: any) =>
- api.put>(`/api/player/colonies/${id}`, colonyData),
-
- delete: (id: number) =>
- api.delete>(`/api/player/colonies/${id}`),
-
- getBuildings: (colonyId: number) =>
- api.get>(`/api/player/colonies/${colonyId}/buildings`),
-
- constructBuilding: (colonyId: number, buildingData: { building_type_id: number }) =>
- api.post>(`/api/player/colonies/${colonyId}/buildings`, buildingData),
-
- upgradeBuilding: (colonyId: number, buildingId: number) =>
- api.put>(`/api/player/colonies/${colonyId}/buildings/${buildingId}/upgrade`),
- },
-
- // Resources
- resources: {
- getByColony: (colonyId: number) =>
- api.get>(`/api/player/colonies/${colonyId}/resources`),
-
- getTotal: () =>
- api.get>('/api/player/resources'),
- },
-
- // Fleets
- fleets: {
- getAll: () =>
- api.get>('/api/player/fleets'),
-
- getById: (id: number) =>
- api.get>(`/api/player/fleets/${id}`),
-
- create: (fleetData: { name: string; colony_id: number; ships: any[] }) =>
- api.post>('/api/player/fleets', fleetData),
-
- update: (id: number, fleetData: any) =>
- api.put>(`/api/player/fleets/${id}`, fleetData),
-
- delete: (id: number) =>
- api.delete>(`/api/player/fleets/${id}`),
-
- move: (id: number, destination: string) =>
- api.post>(`/api/player/fleets/${id}/move`, { destination }),
- },
-
- // Research
- research: {
- getAll: () =>
- api.get>('/api/player/research'),
-
- getTechnologies: () =>
- api.get>('/api/player/research/technologies'),
-
- start: (technologyId: number) =>
- api.post>('/api/player/research/start', { technology_id: technologyId }),
-
- cancel: (researchId: number) =>
- api.post>(`/api/player/research/${researchId}/cancel`),
- },
-
- // Galaxy
- galaxy: {
- getSectors: () =>
- api.get>('/api/player/galaxy/sectors'),
-
- getSector: (coordinates: string) =>
- api.get>(`/api/player/galaxy/sectors/${coordinates}`),
-
- scan: (coordinates: string) =>
- api.post>('/api/player/galaxy/scan', { coordinates }),
- },
-
- // Events
- events: {
- getAll: (limit?: number) =>
- api.get>('/api/player/events', { params: { limit } }),
-
- markRead: (eventId: number) =>
- api.put>(`/api/player/events/${eventId}/read`),
- },
-
- // Notifications
- notifications: {
- getAll: () =>
- api.get>('/api/player/notifications'),
-
- markRead: (notificationId: number) =>
- api.put>(`/api/player/notifications/${notificationId}/read`),
-
- markAllRead: () =>
- api.put>('/api/player/notifications/read-all'),
- },
-};
-
-export default api;
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
deleted file mode 100644
index bef5202..0000000
--- a/frontend/src/main.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.tsx'
-
-createRoot(document.getElementById('root')!).render(
-
-
- ,
-)
diff --git a/frontend/src/pages/Colonies.tsx b/frontend/src/pages/Colonies.tsx
deleted file mode 100644
index fdd96ab..0000000
--- a/frontend/src/pages/Colonies.tsx
+++ /dev/null
@@ -1,257 +0,0 @@
-import React, { useEffect, useState } from 'react';
-import { Link } from 'react-router-dom';
-import {
- BuildingOfficeIcon,
- PlusIcon,
- MapPinIcon,
- UsersIcon,
- HeartIcon,
-} from '@heroicons/react/24/outline';
-import { useGameStore } from '../store/gameStore';
-
-const Colonies: React.FC = () => {
- const {
- colonies,
- loading,
- fetchColonies,
- selectColony,
- } = useGameStore();
-
- const [sortBy, setSortBy] = useState<'name' | 'population' | 'founded_at'>('name');
- const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');
-
- useEffect(() => {
- fetchColonies();
- }, [fetchColonies]);
-
- const sortedColonies = [...colonies].sort((a, b) => {
- let aValue: string | number;
- let bValue: string | number;
-
- switch (sortBy) {
- case 'name':
- aValue = a.name.toLowerCase();
- bValue = b.name.toLowerCase();
- break;
- case 'population':
- aValue = a.population;
- bValue = b.population;
- break;
- case 'founded_at':
- aValue = new Date(a.founded_at).getTime();
- bValue = new Date(b.founded_at).getTime();
- break;
- default:
- aValue = a.name.toLowerCase();
- bValue = b.name.toLowerCase();
- }
-
- if (sortOrder === 'asc') {
- return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
- } else {
- return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
- }
- });
-
- const handleSort = (field: typeof sortBy) => {
- if (sortBy === field) {
- setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
- } else {
- setSortBy(field);
- setSortOrder('asc');
- }
- };
-
- const getMoraleColor = (morale: number) => {
- if (morale >= 80) return 'text-green-400';
- if (morale >= 60) return 'text-yellow-400';
- if (morale >= 40) return 'text-orange-400';
- return 'text-red-400';
- };
-
- const getMoraleIcon = (morale: number) => {
- if (morale >= 80) return '😊';
- if (morale >= 60) return '😐';
- if (morale >= 40) return '😟';
- return '😰';
- };
-
- if (loading.colonies) {
- return (
-
-
-
Colonies
-
-
- {[...Array(6)].map((_, i) => (
-
- ))}
-
-
- );
- }
-
- return (
-
- {/* Header */}
-
-
-
Colonies
-
- Manage your {colonies.length} colonies across the galaxy
-
-
-
-
- Found Colony
-
-
-
- {/* Sort Controls */}
-
-
- Sort by:
-
-
-
-
-
-
- {/* Colonies Grid */}
- {sortedColonies.length > 0 ? (
-
- {sortedColonies.map((colony) => (
-
selectColony(colony)}
- className="card hover:bg-dark-700 transition-colors duration-200 cursor-pointer"
- >
-
- {/* Colony Header */}
-
-
-
{colony.name}
-
-
- {colony.coordinates}
-
-
-
-
-
- {/* Colony Stats */}
-
-
-
-
-
Population
-
- {colony.population.toLocaleString()}
-
-
-
-
-
-
-
-
Morale
-
- {colony.morale}% {getMoraleIcon(colony.morale)}
-
-
-
-
-
- {/* Planet Type */}
- {colony.planet_type && (
-
-
Planet Type
-
{colony.planet_type.name}
-
- )}
-
- {/* Resources Preview */}
- {colony.resources && (
-
-
Resources
-
-
- Scrap:
-
- {colony.resources.scrap.toLocaleString()}
-
-
-
- Energy:
-
- {colony.resources.energy.toLocaleString()}
-
-
-
-
- )}
-
- {/* Founded Date */}
-
-
- Founded {new Date(colony.founded_at).toLocaleDateString()}
-
-
-
-
- ))}
-
- ) : (
-
-
-
No Colonies Yet
-
- Start your galactic empire by founding your first colony
-
-
-
- Found Your First Colony
-
-
- )}
-
- );
-};
-
-export default Colonies;
\ No newline at end of file
diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx
deleted file mode 100644
index 7bd875d..0000000
--- a/frontend/src/pages/Dashboard.tsx
+++ /dev/null
@@ -1,259 +0,0 @@
-import React, { useEffect } from 'react';
-import { Link } from 'react-router-dom';
-import {
- BuildingOfficeIcon,
- RocketLaunchIcon,
- BeakerIcon,
- PlusIcon,
-} from '@heroicons/react/24/outline';
-import { useAuthStore } from '../store/authStore';
-import { useGameStore } from '../store/gameStore';
-
-const Dashboard: React.FC = () => {
- const { user } = useAuthStore();
- const {
- colonies,
- fleets,
- research,
- totalResources,
- loading,
- fetchColonies,
- fetchFleets,
- fetchResearch,
- fetchTotalResources,
- } = useGameStore();
-
- useEffect(() => {
- // Fetch initial data when component mounts
- fetchColonies();
- fetchFleets();
- fetchResearch();
- fetchTotalResources();
- }, [fetchColonies, fetchFleets, fetchResearch, fetchTotalResources]);
-
- const stats = [
- {
- name: 'Colonies',
- value: colonies.length,
- icon: BuildingOfficeIcon,
- href: '/colonies',
- color: 'text-green-400',
- loading: loading.colonies,
- },
- {
- name: 'Fleets',
- value: fleets.length,
- icon: RocketLaunchIcon,
- href: '/fleets',
- color: 'text-blue-400',
- loading: loading.fleets,
- },
- {
- name: 'Research Projects',
- value: research.filter(r => r.is_researching).length,
- icon: BeakerIcon,
- href: '/research',
- color: 'text-purple-400',
- loading: loading.research,
- },
- ];
-
- const recentColonies = colonies.slice(0, 3);
- const activeResearch = research.filter(r => r.is_researching).slice(0, 3);
-
- return (
-
- {/* Welcome Header */}
-
-
- Welcome back, {user?.username}!
-
-
- Command your forces across the shattered galaxy. Your empire awaits your orders.
-
-
-
- {/* Quick Stats */}
-
- {stats.map((stat) => {
- const Icon = stat.icon;
- return (
-
-
-
-
-
-
-
{stat.name}
-
- {stat.loading ? (
-
- ) : (
- stat.value
- )}
-
-
-
-
- );
- })}
-
-
- {/* Resources Overview */}
- {totalResources && (
-
-
Resource Overview
-
-
- Scrap
-
- {totalResources.scrap.toLocaleString()}
-
-
-
- Energy
-
- {totalResources.energy.toLocaleString()}
-
-
-
- Research
-
- {totalResources.research_points.toLocaleString()}
-
-
-
- Biomass
-
- {totalResources.biomass.toLocaleString()}
-
-
-
-
- )}
-
-
- {/* Recent Colonies */}
-
-
-
Recent Colonies
-
- View all
-
-
-
- {loading.colonies ? (
-
- {[...Array(3)].map((_, i) => (
-
- ))}
-
- ) : recentColonies.length > 0 ? (
-
- {recentColonies.map((colony) => (
-
-
-
-
{colony.name}
-
{colony.coordinates}
-
-
-
Population
-
- {colony.population.toLocaleString()}
-
-
-
-
- ))}
-
- ) : (
-
-
-
No colonies yet
-
-
- Found your first colony
-
-
- )}
-
-
- {/* Active Research */}
-
-
-
Active Research
-
- View all
-
-
-
- {loading.research ? (
-
- {[...Array(3)].map((_, i) => (
-
- ))}
-
- ) : activeResearch.length > 0 ? (
-
- {activeResearch.map((research) => (
-
-
-
-
- {research.technology?.name}
-
-
- Level {research.level}
-
-
-
-
-
- ))}
-
- ) : (
-
-
-
No active research
-
-
- Start research
-
-
- )}
-
-
-
- );
-};
-
-export default Dashboard;
\ No newline at end of file
diff --git a/frontend/src/store/authStore.ts b/frontend/src/store/authStore.ts
deleted file mode 100644
index 7990e44..0000000
--- a/frontend/src/store/authStore.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-import { create } from 'zustand';
-import { persist } from 'zustand/middleware';
-import type { AuthState, LoginCredentials, RegisterCredentials } from '../types';
-import { apiClient } from '../lib/api';
-import toast from 'react-hot-toast';
-
-interface AuthStore extends AuthState {
- // Actions
- login: (credentials: LoginCredentials) => Promise;
- register: (credentials: RegisterCredentials) => Promise;
- logout: () => void;
- refreshUser: () => Promise;
- clearError: () => void;
- setLoading: (loading: boolean) => void;
-}
-
-export const useAuthStore = create()(
- persist(
- (set) => ({
- // Initial state
- user: null,
- token: null,
- isAuthenticated: false,
- isLoading: false,
-
- // Login action
- login: async (credentials: LoginCredentials) => {
- set({ isLoading: true });
-
- try {
- const response = await apiClient.auth.login(credentials);
-
- if (response.data.success && response.data.data) {
- const { user, token } = response.data.data;
-
- // Store token in localStorage for API client
- localStorage.setItem('auth_token', token);
-
- set({
- user,
- token,
- isAuthenticated: true,
- isLoading: false,
- });
-
- toast.success(`Welcome back, ${user.username}!`);
- return true;
- } else {
- toast.error(response.data.error || 'Login failed');
- set({ isLoading: false });
- return false;
- }
- } catch (error: any) {
- const message = error.response?.data?.error || 'Login failed';
- toast.error(message);
- set({ isLoading: false });
- return false;
- }
- },
-
- // Register action
- register: async (credentials: RegisterCredentials) => {
- set({ isLoading: true });
-
- try {
- const { confirmPassword, ...registerData } = credentials;
-
- // Validate passwords match
- if (credentials.password !== confirmPassword) {
- toast.error('Passwords do not match');
- set({ isLoading: false });
- return false;
- }
-
- const response = await apiClient.auth.register(registerData);
-
- if (response.data.success && response.data.data) {
- const { user, token } = response.data.data;
-
- // Store token in localStorage for API client
- localStorage.setItem('auth_token', token);
-
- set({
- user,
- token,
- isAuthenticated: true,
- isLoading: false,
- });
-
- toast.success(`Welcome to Shattered Void, ${user.username}!`);
- return true;
- } else {
- toast.error(response.data.error || 'Registration failed');
- set({ isLoading: false });
- return false;
- }
- } catch (error: any) {
- const message = error.response?.data?.error || 'Registration failed';
- toast.error(message);
- set({ isLoading: false });
- return false;
- }
- },
-
- // Logout action
- logout: () => {
- try {
- // Call logout endpoint to invalidate token on server
- apiClient.auth.logout().catch(() => {
- // Ignore errors on logout endpoint
- });
- } catch (error) {
- // Ignore errors
- }
-
- // Clear local storage
- localStorage.removeItem('auth_token');
- localStorage.removeItem('user_data');
-
- // Clear store state
- set({
- user: null,
- token: null,
- isAuthenticated: false,
- isLoading: false,
- });
-
- toast.success('Logged out successfully');
- },
-
- // Refresh user data
- refreshUser: async () => {
- const token = localStorage.getItem('auth_token');
- if (!token) return;
-
- try {
- const response = await apiClient.player.getProfile();
-
- if (response.data.success && response.data.data) {
- set({ user: response.data.data });
- }
- } catch (error) {
- // If refresh fails, user might need to re-login
- console.error('Failed to refresh user data:', error);
- }
- },
-
- // Clear error state
- clearError: () => {
- // This can be extended if we add error state
- },
-
- // Set loading state
- setLoading: (loading: boolean) => {
- set({ isLoading: loading });
- },
- }),
- {
- name: 'auth-storage',
- partialize: (state) => ({
- user: state.user,
- token: state.token,
- isAuthenticated: state.isAuthenticated,
- }),
- }
- )
-);
\ No newline at end of file
diff --git a/frontend/src/store/gameStore.ts b/frontend/src/store/gameStore.ts
deleted file mode 100644
index 92e1d71..0000000
--- a/frontend/src/store/gameStore.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-import { create } from 'zustand';
-import type { Colony, Fleet, Resources, Research } from '../types';
-import { apiClient } from '../lib/api';
-import toast from 'react-hot-toast';
-
-interface GameState {
- // Data
- colonies: Colony[];
- fleets: Fleet[];
- totalResources: Resources | null;
- research: Research[];
-
- // Loading states
- loading: {
- colonies: boolean;
- fleets: boolean;
- resources: boolean;
- research: boolean;
- };
-
- // Selected entities
- selectedColony: Colony | null;
- selectedFleet: Fleet | null;
-}
-
-interface GameStore extends GameState {
- // Colony actions
- fetchColonies: () => Promise;
- selectColony: (colony: Colony | null) => void;
- createColony: (colonyData: { name: string; coordinates: string; planet_type_id: number }) => Promise;
- updateColony: (colonyId: number, updates: Partial) => void;
-
- // Fleet actions
- fetchFleets: () => Promise;
- selectFleet: (fleet: Fleet | null) => void;
- createFleet: (fleetData: { name: string; colony_id: number; ships: any[] }) => Promise;
- updateFleet: (fleetId: number, updates: Partial) => void;
-
- // Resource actions
- fetchTotalResources: () => Promise;
- updateColonyResources: (colonyId: number, resources: Resources) => void;
-
- // Research actions
- fetchResearch: () => Promise;
- startResearch: (technologyId: number) => Promise;
- updateResearch: (researchId: number, updates: Partial) => void;
-
- // Utility actions
- setLoading: (key: keyof GameState['loading'], loading: boolean) => void;
- clearData: () => void;
-}
-
-export const useGameStore = create((set, get) => ({
- // Initial state
- colonies: [],
- fleets: [],
- totalResources: null,
- research: [],
- loading: {
- colonies: false,
- fleets: false,
- resources: false,
- research: false,
- },
- selectedColony: null,
- selectedFleet: null,
-
- // Colony actions
- fetchColonies: async () => {
- set(state => ({ loading: { ...state.loading, colonies: true } }));
-
- try {
- const response = await apiClient.colonies.getAll();
-
- if (response.data.success && response.data.data) {
- set({
- colonies: response.data.data,
- loading: { ...get().loading, colonies: false }
- });
- }
- } catch (error: any) {
- console.error('Failed to fetch colonies:', error);
- toast.error('Failed to load colonies');
- set(state => ({ loading: { ...state.loading, colonies: false } }));
- }
- },
-
- selectColony: (colony: Colony | null) => {
- set({ selectedColony: colony });
- },
-
- createColony: async (colonyData) => {
- try {
- const response = await apiClient.colonies.create(colonyData);
-
- if (response.data.success && response.data.data) {
- const newColony = response.data.data;
- set(state => ({
- colonies: [...state.colonies, newColony]
- }));
- toast.success(`Colony "${colonyData.name}" founded successfully!`);
- return true;
- } else {
- toast.error(response.data.error || 'Failed to create colony');
- return false;
- }
- } catch (error: any) {
- const message = error.response?.data?.error || 'Failed to create colony';
- toast.error(message);
- return false;
- }
- },
-
- updateColony: (colonyId: number, updates: Partial) => {
- set(state => ({
- colonies: state.colonies.map(colony =>
- colony.id === colonyId ? { ...colony, ...updates } : colony
- ),
- selectedColony: state.selectedColony?.id === colonyId
- ? { ...state.selectedColony, ...updates }
- : state.selectedColony
- }));
- },
-
- // Fleet actions
- fetchFleets: async () => {
- set(state => ({ loading: { ...state.loading, fleets: true } }));
-
- try {
- const response = await apiClient.fleets.getAll();
-
- if (response.data.success && response.data.data) {
- set({
- fleets: response.data.data,
- loading: { ...get().loading, fleets: false }
- });
- }
- } catch (error: any) {
- console.error('Failed to fetch fleets:', error);
- toast.error('Failed to load fleets');
- set(state => ({ loading: { ...state.loading, fleets: false } }));
- }
- },
-
- selectFleet: (fleet: Fleet | null) => {
- set({ selectedFleet: fleet });
- },
-
- createFleet: async (fleetData) => {
- try {
- const response = await apiClient.fleets.create(fleetData);
-
- if (response.data.success && response.data.data) {
- const newFleet = response.data.data;
- set(state => ({
- fleets: [...state.fleets, newFleet]
- }));
- toast.success(`Fleet "${fleetData.name}" created successfully!`);
- return true;
- } else {
- toast.error(response.data.error || 'Failed to create fleet');
- return false;
- }
- } catch (error: any) {
- const message = error.response?.data?.error || 'Failed to create fleet';
- toast.error(message);
- return false;
- }
- },
-
- updateFleet: (fleetId: number, updates: Partial) => {
- set(state => ({
- fleets: state.fleets.map(fleet =>
- fleet.id === fleetId ? { ...fleet, ...updates } : fleet
- ),
- selectedFleet: state.selectedFleet?.id === fleetId
- ? { ...state.selectedFleet, ...updates }
- : state.selectedFleet
- }));
- },
-
- // Resource actions
- fetchTotalResources: async () => {
- set(state => ({ loading: { ...state.loading, resources: true } }));
-
- try {
- const response = await apiClient.resources.getTotal();
-
- if (response.data.success && response.data.data) {
- set({
- totalResources: response.data.data,
- loading: { ...get().loading, resources: false }
- });
- }
- } catch (error: any) {
- console.error('Failed to fetch resources:', error);
- set(state => ({ loading: { ...state.loading, resources: false } }));
- }
- },
-
- updateColonyResources: (colonyId: number, resources: Resources) => {
- set(state => ({
- colonies: state.colonies.map(colony =>
- colony.id === colonyId
- ? {
- ...colony,
- resources: colony.resources
- ? { ...colony.resources, ...resources }
- : undefined
- }
- : colony
- )
- }));
- },
-
- // Research actions
- fetchResearch: async () => {
- set(state => ({ loading: { ...state.loading, research: true } }));
-
- try {
- const response = await apiClient.research.getAll();
-
- if (response.data.success && response.data.data) {
- set({
- research: response.data.data,
- loading: { ...get().loading, research: false }
- });
- }
- } catch (error: any) {
- console.error('Failed to fetch research:', error);
- toast.error('Failed to load research');
- set(state => ({ loading: { ...state.loading, research: false } }));
- }
- },
-
- startResearch: async (technologyId: number) => {
- try {
- const response = await apiClient.research.start(technologyId);
-
- if (response.data.success && response.data.data) {
- const newResearch = response.data.data;
- set(state => ({
- research: [...state.research, newResearch]
- }));
- toast.success('Research started successfully!');
- return true;
- } else {
- toast.error(response.data.error || 'Failed to start research');
- return false;
- }
- } catch (error: any) {
- const message = error.response?.data?.error || 'Failed to start research';
- toast.error(message);
- return false;
- }
- },
-
- updateResearch: (researchId: number, updates: Partial) => {
- set(state => ({
- research: state.research.map(research =>
- research.id === researchId ? { ...research, ...updates } : research
- )
- }));
- },
-
- // Utility actions
- setLoading: (key: keyof GameState['loading'], loading: boolean) => {
- set(state => ({
- loading: { ...state.loading, [key]: loading }
- }));
- },
-
- clearData: () => {
- set({
- colonies: [],
- fleets: [],
- totalResources: null,
- research: [],
- selectedColony: null,
- selectedFleet: null,
- loading: {
- colonies: false,
- fleets: false,
- resources: false,
- research: false,
- }
- });
- },
-}));
\ No newline at end of file
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
deleted file mode 100644
index 0f1e857..0000000
--- a/frontend/src/types/index.ts
+++ /dev/null
@@ -1,200 +0,0 @@
-// Authentication types
-export interface User {
- id: number;
- username: string;
- email: string;
- created_at: string;
- last_login?: string;
-}
-
-export interface AuthState {
- user: User | null;
- token: string | null;
- isAuthenticated: boolean;
- isLoading: boolean;
-}
-
-export interface LoginCredentials {
- email: string;
- password: string;
-}
-
-export interface RegisterCredentials {
- username: string;
- email: string;
- password: string;
- confirmPassword: string;
-}
-
-// Colony types
-export interface Colony {
- id: number;
- player_id: number;
- name: string;
- coordinates: string;
- planet_type_id: number;
- population: number;
- morale: number;
- founded_at: string;
- last_updated: string;
- planet_type?: PlanetType;
- buildings?: Building[];
- resources?: ColonyResources;
-}
-
-export interface PlanetType {
- id: number;
- name: string;
- description: string;
- resource_modifiers: Record;
-}
-
-export interface Building {
- id: number;
- colony_id: number;
- building_type_id: number;
- level: number;
- construction_start?: string;
- construction_end?: string;
- is_constructing: boolean;
- building_type?: BuildingType;
-}
-
-export interface BuildingType {
- id: number;
- name: string;
- description: string;
- category: string;
- base_cost: Record;
- base_production: Record;
- max_level: number;
-}
-
-// Resource types
-export interface Resources {
- scrap: number;
- energy: number;
- research_points: number;
- biomass: number;
-}
-
-export interface ColonyResources extends Resources {
- colony_id: number;
- last_updated: string;
- production_rates: Resources;
-}
-
-// Fleet types
-export interface Fleet {
- id: number;
- player_id: number;
- name: string;
- location_type: 'colony' | 'space';
- location_id?: number;
- coordinates?: string;
- status: 'docked' | 'moving' | 'in_combat';
- destination?: string;
- arrival_time?: string;
- ships: FleetShip[];
-}
-
-export interface FleetShip {
- id: number;
- fleet_id: number;
- design_id: number;
- quantity: number;
- ship_design?: ShipDesign;
-}
-
-export interface ShipDesign {
- id: number;
- name: string;
- hull_type: string;
- cost: Record;
- stats: {
- attack: number;
- defense: number;
- health: number;
- speed: number;
- cargo: number;
- };
-}
-
-// Research types
-export interface Research {
- id: number;
- player_id: number;
- technology_id: number;
- level: number;
- research_start?: string;
- research_end?: string;
- is_researching: boolean;
- technology?: Technology;
-}
-
-export interface Technology {
- id: number;
- name: string;
- description: string;
- category: string;
- base_cost: number;
- max_level: number;
- prerequisites: number[];
- unlocks: string[];
-}
-
-// WebSocket types
-export interface WebSocketMessage {
- type: string;
- data: any;
- timestamp: string;
-}
-
-export interface GameEvent {
- id: string;
- type: 'colony_update' | 'resource_update' | 'fleet_update' | 'research_complete' | 'building_complete';
- data: any;
- timestamp: string;
-}
-
-// API Response types
-export interface ApiResponse {
- success: boolean;
- data?: T;
- error?: string;
- message?: string;
-}
-
-export interface PaginatedResponse {
- data: T[];
- total: number;
- page: number;
- limit: number;
- totalPages: number;
-}
-
-// UI State types
-export interface LoadingState {
- [key: string]: boolean;
-}
-
-export interface ErrorState {
- [key: string]: string | null;
-}
-
-// Navigation types
-export interface NavItem {
- name: string;
- href: string;
- icon?: React.ComponentType;
- current?: boolean;
- badge?: number;
-}
-
-// Toast notification types
-export interface ToastOptions {
- type: 'success' | 'error' | 'warning' | 'info';
- title: string;
- message?: string;
- duration?: number;
-}
\ No newline at end of file
diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe..0000000
--- a/frontend/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
deleted file mode 100644
index 0d00cf6..0000000
--- a/frontend/tailwind.config.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/** @type {import('tailwindcss').Config} */
-export default {
- content: [
- "./index.html",
- "./src/**/*.{js,ts,jsx,tsx}",
- ],
- theme: {
- extend: {
- colors: {
- primary: {
- 50: '#eff6ff',
- 100: '#dbeafe',
- 200: '#bfdbfe',
- 300: '#93c5fd',
- 400: '#60a5fa',
- 500: '#3b82f6',
- 600: '#2563eb',
- 700: '#1d4ed8',
- 800: '#1e40af',
- 900: '#1e3a8a',
- },
- dark: {
- 50: '#f8fafc',
- 100: '#f1f5f9',
- 200: '#e2e8f0',
- 300: '#cbd5e1',
- 400: '#94a3b8',
- 500: '#64748b',
- 600: '#475569',
- 700: '#334155',
- 800: '#1e293b',
- 900: '#0f172a',
- }
- },
- fontFamily: {
- 'mono': ['JetBrains Mono', 'Fira Code', 'Monaco', 'Consolas', 'monospace'],
- },
- animation: {
- 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
- 'fade-in': 'fadeIn 0.5s ease-out',
- 'slide-in': 'slideIn 0.3s ease-out',
- },
- keyframes: {
- fadeIn: {
- '0%': { opacity: '0' },
- '100%': { opacity: '1' },
- },
- slideIn: {
- '0%': { transform: 'translateY(-10px)', opacity: '0' },
- '100%': { transform: 'translateY(0)', opacity: '1' },
- }
- }
- },
- },
- plugins: [],
-}
\ No newline at end of file
diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json
deleted file mode 100644
index 227a6c6..0000000
--- a/frontend/tsconfig.app.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "ES2022",
- "useDefineForClassFields": true,
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "moduleDetection": "force",
- "noEmit": true,
- "jsx": "react-jsx",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "erasableSyntaxOnly": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": ["src"]
-}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
deleted file mode 100644
index 1ffef60..0000000
--- a/frontend/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "files": [],
- "references": [
- { "path": "./tsconfig.app.json" },
- { "path": "./tsconfig.node.json" }
- ]
-}
diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json
deleted file mode 100644
index f85a399..0000000
--- a/frontend/tsconfig.node.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "target": "ES2023",
- "lib": ["ES2023"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "moduleDetection": "force",
- "noEmit": true,
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "erasableSyntaxOnly": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
deleted file mode 100644
index 3c72d2f..0000000
--- a/frontend/vite.config.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-import path from 'path'
-
-// https://vite.dev/config/
-export default defineConfig({
- plugins: [react()],
- server: {
- port: 5173,
- host: true,
- proxy: {
- '/api': {
- target: 'http://localhost:3000',
- changeOrigin: true,
- secure: false,
- },
- '/socket.io': {
- target: 'http://localhost:3000',
- changeOrigin: true,
- ws: true,
- }
- }
- },
- resolve: {
- alias: {
- '@': path.resolve(__dirname, './src'),
- },
- },
- build: {
- outDir: 'dist',
- sourcemap: true,
- rollupOptions: {
- output: {
- manualChunks: {
- vendor: ['react', 'react-dom'],
- router: ['react-router-dom'],
- ui: ['@headlessui/react', '@heroicons/react'],
- },
- },
- },
- },
- optimizeDeps: {
- include: ['react', 'react-dom', 'react-router-dom'],
- },
-})
diff --git a/package.json b/package.json
index f03525f..012b56d 100644
--- a/package.json
+++ b/package.json
@@ -6,37 +6,24 @@
"scripts": {
"dev": "nodemon --inspect=0.0.0.0:9229 src/server.js",
"start": "node src/server.js",
- "start:game": "node start-game.js",
- "start:dev": "NODE_ENV=development node start-game.js",
- "start:prod": "NODE_ENV=production node start-game.js",
- "start:staging": "NODE_ENV=staging node start-game.js",
- "start:quick": "./start.sh",
- "start:debug": "./start.sh --debug --verbose",
- "start:no-frontend": "./start.sh --no-frontend",
- "start:backend-only": "ENABLE_FRONTEND=false node start-game.js",
- "game": "./start.sh",
"test": "jest --verbose --coverage",
"test:watch": "jest --watch --verbose",
"test:integration": "jest --testPathPattern=integration --runInBand",
"test:e2e": "jest --testPathPattern=e2e --runInBand",
"lint": "eslint src/ --ext .js --fix",
"lint:check": "eslint src/ --ext .js",
- "health:check": "node -e \"require('./scripts/health-monitor'); console.log('Health monitoring available')\"",
- "system:check": "node -e \"const checks = require('./scripts/startup-checks'); new checks().runAllChecks().then(r => console.log('System checks:', r.success ? 'PASSED' : 'FAILED'))\"",
"db:migrate": "knex migrate:latest",
"db:rollback": "knex migrate:rollback",
"db:seed": "knex seed:run",
"db:reset": "knex migrate:rollback:all && knex migrate:latest && knex seed:run",
"db:setup": "createdb shattered_void_dev && npm run db:migrate && npm run db:seed",
- "db:validate": "node -e \"const val = require('./scripts/database-validator'); new val().validateDatabase().then(r => console.log('DB validation:', r.success ? 'PASSED' : 'FAILED', r.error || ''))\"",
"setup": "node scripts/setup.js",
"docker:build": "docker build -t shattered-void .",
"docker:run": "docker-compose up -d",
"docker:dev": "docker-compose -f docker-compose.dev.yml up -d",
"logs": "tail -f logs/combined.log",
"logs:error": "tail -f logs/error.log",
- "logs:audit": "tail -f logs/audit.log",
- "logs:startup": "tail -f logs/startup.log"
+ "logs:audit": "tail -f logs/audit.log"
},
"dependencies": {
"bcrypt": "^5.1.1",
diff --git a/scripts/database-validator.js b/scripts/database-validator.js
deleted file mode 100644
index 50bd138..0000000
--- a/scripts/database-validator.js
+++ /dev/null
@@ -1,622 +0,0 @@
-/**
- * Shattered Void MMO - Database Validation System
- *
- * This module provides comprehensive database validation including connectivity,
- * schema validation, migration status, and data integrity checks.
- */
-
-const path = require('path');
-const fs = require('fs').promises;
-
-class DatabaseValidator {
- constructor() {
- this.knex = null;
- this.validationResults = {
- connectivity: false,
- migrations: false,
- schema: false,
- seeds: false,
- integrity: false
- };
- }
-
- /**
- * Validate complete database setup
- */
- async validateDatabase() {
- const startTime = Date.now();
- const results = {
- success: false,
- connectivity: null,
- migrations: null,
- schema: null,
- seeds: null,
- integrity: null,
- error: null,
- duration: 0
- };
-
- try {
- // Test database connectivity
- results.connectivity = await this.validateConnectivity();
-
- // Check migration status
- results.migrations = await this.validateMigrations();
-
- // Validate schema structure
- results.schema = await this.validateSchema();
-
- // Check seed data
- results.seeds = await this.validateSeeds();
-
- // Run integrity checks
- results.integrity = await this.validateIntegrity();
-
- // Determine overall success
- results.success = results.connectivity.success &&
- results.migrations.success &&
- results.schema.success;
-
- results.duration = Date.now() - startTime;
- return results;
-
- } catch (error) {
- results.error = error.message;
- results.duration = Date.now() - startTime;
- return results;
- } finally {
- // Cleanup database connection
- if (this.knex) {
- await this.knex.destroy();
- }
- }
- }
-
- /**
- * Validate database connectivity
- */
- async validateConnectivity() {
- try {
- // Load database configuration
- const knexConfig = this.loadKnexConfig();
- const config = knexConfig[process.env.NODE_ENV || 'development'];
-
- if (!config) {
- throw new Error(`No database configuration found for environment: ${process.env.NODE_ENV || 'development'}`);
- }
-
- // Initialize Knex connection
- this.knex = require('knex')(config);
-
- // Test basic connectivity
- await this.knex.raw('SELECT 1 as test');
-
- // Get database version info
- const versionResult = await this.knex.raw('SELECT version()');
- const version = versionResult.rows[0].version;
-
- // Get database size info
- const sizeResult = await this.knex.raw(`
- SELECT pg_database.datname,
- pg_size_pretty(pg_database_size(pg_database.datname)) AS size
- FROM pg_database
- WHERE pg_database.datname = current_database()
- `);
-
- const dbSize = sizeResult.rows[0]?.size || 'Unknown';
-
- // Check connection pool status
- const poolInfo = {
- min: this.knex.client.pool.min,
- max: this.knex.client.pool.max,
- used: this.knex.client.pool.numUsed(),
- free: this.knex.client.pool.numFree(),
- pending: this.knex.client.pool.numPendingAcquires()
- };
-
- return {
- success: true,
- database: config.connection.database,
- host: config.connection.host,
- port: config.connection.port,
- version: version.split(' ')[0] + ' ' + version.split(' ')[1], // PostgreSQL version
- size: dbSize,
- pool: poolInfo,
- ssl: config.connection.ssl ? 'enabled' : 'disabled'
- };
-
- } catch (error) {
- return {
- success: false,
- error: error.message,
- troubleshooting: this.getDatabaseTroubleshooting(error)
- };
- }
- }
-
- /**
- * Validate migration status
- */
- async validateMigrations() {
- try {
- // Check if migrations table exists
- const hasTable = await this.knex.schema.hasTable('knex_migrations');
-
- if (!hasTable) {
- // Run migrations if table doesn't exist
- console.log(' 📦 Running initial database migrations...');
- await this.knex.migrate.latest();
- }
-
- // Get migration status
- const [currentBatch, migrationList] = await Promise.all([
- this.knex.migrate.currentVersion(),
- this.knex.migrate.list()
- ]);
-
- const [completed, pending] = migrationList;
-
- // Check for pending migrations
- if (pending.length > 0) {
- console.log(` 📦 Found ${pending.length} pending migrations, running now...`);
- await this.knex.migrate.latest();
-
- // Re-check status after running migrations
- const [newCompleted] = await this.knex.migrate.list();
-
- return {
- success: true,
- currentBatch: await this.knex.migrate.currentVersion(),
- completed: newCompleted.length,
- pending: 0,
- autoRan: pending.length,
- migrations: newCompleted.map(migration => ({
- name: migration,
- status: 'completed'
- }))
- };
- }
-
- return {
- success: true,
- currentBatch,
- completed: completed.length,
- pending: pending.length,
- migrations: [
- ...completed.map(migration => ({
- name: migration,
- status: 'completed'
- })),
- ...pending.map(migration => ({
- name: migration,
- status: 'pending'
- }))
- ]
- };
-
- } catch (error) {
- return {
- success: false,
- error: error.message,
- troubleshooting: [
- 'Check if migration files exist in src/database/migrations/',
- 'Verify database user has CREATE permissions',
- 'Ensure migration files follow correct naming convention'
- ]
- };
- }
- }
-
- /**
- * Validate database schema structure
- */
- async validateSchema() {
- try {
- const requiredTables = [
- 'players',
- 'colonies',
- 'player_resources',
- 'fleets',
- 'fleet_ships',
- 'ship_designs',
- 'technologies',
- 'player_research'
- ];
-
- const schemaInfo = {
- tables: {},
- missingTables: [],
- totalTables: 0,
- requiredTables: requiredTables.length
- };
-
- // Check each required table
- for (const tableName of requiredTables) {
- const exists = await this.knex.schema.hasTable(tableName);
-
- if (exists) {
- // Get table info
- const columns = await this.knex(tableName).columnInfo();
- const rowCount = await this.knex(tableName).count('* as count').first();
-
- schemaInfo.tables[tableName] = {
- exists: true,
- columns: Object.keys(columns).length,
- rows: parseInt(rowCount.count),
- structure: Object.keys(columns)
- };
- } else {
- schemaInfo.missingTables.push(tableName);
- schemaInfo.tables[tableName] = {
- exists: false,
- error: 'Table does not exist'
- };
- }
- }
-
- // Get total number of tables in database
- const allTables = await this.knex.raw(`
- SELECT table_name
- FROM information_schema.tables
- WHERE table_schema = 'public'
- AND table_type = 'BASE TABLE'
- `);
-
- schemaInfo.totalTables = allTables.rows.length;
-
- const success = schemaInfo.missingTables.length === 0;
-
- return {
- success,
- ...schemaInfo,
- coverage: `${requiredTables.length - schemaInfo.missingTables.length}/${requiredTables.length}`,
- troubleshooting: !success ? [
- 'Run database migrations: npm run db:migrate',
- 'Check migration files in src/database/migrations/',
- 'Verify database user has CREATE permissions'
- ] : null
- };
-
- } catch (error) {
- return {
- success: false,
- error: error.message
- };
- }
- }
-
- /**
- * Validate seed data
- */
- async validateSeeds() {
- try {
- const seedChecks = {
- technologies: await this.checkTechnologiesSeeded(),
- shipDesigns: await this.checkShipDesignsSeeded(),
- systemData: await this.checkSystemDataSeeded()
- };
-
- const allSeeded = Object.values(seedChecks).every(check => check.seeded);
-
- // If no seed data, offer to run seeds
- if (!allSeeded) {
- console.log(' 🌱 Some seed data is missing, running seeds...');
-
- try {
- // Run seeds
- await this.knex.seed.run();
-
- // Re-check seed status
- const newSeedChecks = {
- technologies: await this.checkTechnologiesSeeded(),
- shipDesigns: await this.checkShipDesignsSeeded(),
- systemData: await this.checkSystemDataSeeded()
- };
-
- return {
- success: true,
- autoSeeded: true,
- checks: newSeedChecks,
- message: 'Seed data was missing and has been automatically populated'
- };
-
- } catch (seedError) {
- return {
- success: false,
- autoSeeded: false,
- error: `Failed to run seeds: ${seedError.message}`,
- checks: seedChecks
- };
- }
- }
-
- return {
- success: true,
- checks: seedChecks,
- message: 'All required seed data is present'
- };
-
- } catch (error) {
- return {
- success: false,
- error: error.message
- };
- }
- }
-
- /**
- * Validate data integrity
- */
- async validateIntegrity() {
- try {
- const integrityChecks = [];
-
- // Check foreign key constraints
- integrityChecks.push(await this.checkForeignKeyIntegrity());
-
- // Check for orphaned records
- integrityChecks.push(await this.checkOrphanedRecords());
-
- // Check data consistency
- integrityChecks.push(await this.checkDataConsistency());
-
- const allPassed = integrityChecks.every(check => check.passed);
-
- return {
- success: allPassed,
- checks: integrityChecks,
- summary: `${integrityChecks.filter(c => c.passed).length}/${integrityChecks.length} integrity checks passed`
- };
-
- } catch (error) {
- return {
- success: false,
- error: error.message
- };
- }
- }
-
- /**
- * Check if technologies are seeded
- */
- async checkTechnologiesSeeded() {
- try {
- const count = await this.knex('technologies').count('* as count').first();
- const techCount = parseInt(count.count);
-
- return {
- seeded: techCount > 0,
- count: techCount,
- expected: '> 0'
- };
- } catch (error) {
- return {
- seeded: false,
- error: error.message
- };
- }
- }
-
- /**
- * Check if ship designs are seeded
- */
- async checkShipDesignsSeeded() {
- try {
- const count = await this.knex('ship_designs').count('* as count').first();
- const designCount = parseInt(count.count);
-
- return {
- seeded: designCount > 0,
- count: designCount,
- expected: '> 0'
- };
- } catch (error) {
- return {
- seeded: false,
- error: error.message
- };
- }
- }
-
- /**
- * Check if system data is seeded
- */
- async checkSystemDataSeeded() {
- try {
- // Check if we have any basic game configuration
- const hasBasicData = true; // For now, assume system data is OK if DB is accessible
-
- return {
- seeded: hasBasicData,
- message: 'System data validation passed'
- };
- } catch (error) {
- return {
- seeded: false,
- error: error.message
- };
- }
- }
-
- /**
- * Check foreign key integrity
- */
- async checkForeignKeyIntegrity() {
- try {
- // Check for any foreign key constraint violations
- const violations = [];
-
- // Check colonies -> players
- const orphanedColonies = await this.knex.raw(`
- SELECT c.id, c.name FROM colonies c
- LEFT JOIN players p ON c.player_id = p.id
- WHERE p.id IS NULL
- `);
-
- if (orphanedColonies.rows.length > 0) {
- violations.push(`${orphanedColonies.rows.length} colonies without valid players`);
- }
-
- // Check fleets -> players
- const orphanedFleets = await this.knex.raw(`
- SELECT f.id, f.name FROM fleets f
- LEFT JOIN players p ON f.player_id = p.id
- WHERE p.id IS NULL
- `);
-
- if (orphanedFleets.rows.length > 0) {
- violations.push(`${orphanedFleets.rows.length} fleets without valid players`);
- }
-
- return {
- passed: violations.length === 0,
- name: 'Foreign Key Integrity',
- violations: violations,
- message: violations.length === 0 ? 'All foreign key constraints are valid' : `Found ${violations.length} violations`
- };
-
- } catch (error) {
- return {
- passed: false,
- name: 'Foreign Key Integrity',
- error: error.message
- };
- }
- }
-
- /**
- * Check for orphaned records
- */
- async checkOrphanedRecords() {
- try {
- const orphanedRecords = [];
-
- // This is a simplified check - in a real scenario you'd check all relationships
- return {
- passed: orphanedRecords.length === 0,
- name: 'Orphaned Records Check',
- orphaned: orphanedRecords,
- message: 'No orphaned records found'
- };
-
- } catch (error) {
- return {
- passed: false,
- name: 'Orphaned Records Check',
- error: error.message
- };
- }
- }
-
- /**
- * Check data consistency
- */
- async checkDataConsistency() {
- try {
- const inconsistencies = [];
-
- // Example: Check if all players have at least one colony (if required by game rules)
- // This would depend on your specific game rules
-
- return {
- passed: inconsistencies.length === 0,
- name: 'Data Consistency Check',
- inconsistencies: inconsistencies,
- message: 'Data consistency checks passed'
- };
-
- } catch (error) {
- return {
- passed: false,
- name: 'Data Consistency Check',
- error: error.message
- };
- }
- }
-
- /**
- * Load Knex configuration
- */
- loadKnexConfig() {
- try {
- const knexfilePath = path.join(process.cwd(), 'knexfile.js');
- delete require.cache[require.resolve(knexfilePath)];
- return require(knexfilePath);
- } catch (error) {
- throw new Error(`Cannot load knexfile.js: ${error.message}`);
- }
- }
-
- /**
- * Get database troubleshooting tips
- */
- getDatabaseTroubleshooting(error) {
- const tips = [];
-
- if (error.message.includes('ECONNREFUSED')) {
- tips.push('Database server is not running - start PostgreSQL service');
- tips.push('Check if database is running on correct host/port');
- }
-
- if (error.message.includes('authentication failed')) {
- tips.push('Check database username and password in .env file');
- tips.push('Verify database user exists and has correct permissions');
- }
-
- if (error.message.includes('database') && error.message.includes('does not exist')) {
- tips.push('Create database: createdb shattered_void_dev');
- tips.push('Or run: npm run db:setup');
- }
-
- if (error.message.includes('permission denied')) {
- tips.push('Database user needs CREATE and ALTER permissions');
- tips.push('Check PostgreSQL user privileges');
- }
-
- if (tips.length === 0) {
- tips.push('Check database connection parameters in .env file');
- tips.push('Ensure PostgreSQL is installed and running');
- tips.push('Verify network connectivity to database server');
- }
-
- return tips;
- }
-
- /**
- * Get database performance metrics
- */
- async getDatabaseMetrics() {
- if (!this.knex) {
- return null;
- }
-
- try {
- // Get connection info
- const connections = await this.knex.raw(`
- SELECT count(*) as total,
- count(*) FILTER (WHERE state = 'active') as active,
- count(*) FILTER (WHERE state = 'idle') as idle
- FROM pg_stat_activity
- WHERE datname = current_database()
- `);
-
- // Get database size
- const size = await this.knex.raw(`
- SELECT pg_size_pretty(pg_database_size(current_database())) as size
- `);
-
- return {
- connections: connections.rows[0],
- size: size.rows[0].size,
- timestamp: new Date().toISOString()
- };
-
- } catch (error) {
- return {
- error: error.message
- };
- }
- }
-}
-
-module.exports = DatabaseValidator;
\ No newline at end of file
diff --git a/scripts/debug-database.js b/scripts/debug-database.js
deleted file mode 100755
index 126d0cb..0000000
--- a/scripts/debug-database.js
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * Comprehensive Database Debugging Tool
- *
- * This tool provides detailed database diagnostics and troubleshooting
- * capabilities for the Shattered Void MMO.
- */
-
-require('dotenv').config();
-const DatabaseValidator = require('./database-validator');
-
-// Color codes for console output
-const colors = {
- reset: '\x1b[0m',
- bright: '\x1b[1m',
- red: '\x1b[31m',
- green: '\x1b[32m',
- yellow: '\x1b[33m',
- blue: '\x1b[34m',
- magenta: '\x1b[35m',
- cyan: '\x1b[36m',
- white: '\x1b[37m'
-};
-
-function log(level, message) {
- let colorCode = colors.white;
- let prefix = 'INFO';
-
- switch (level) {
- case 'error':
- colorCode = colors.red;
- prefix = 'ERROR';
- break;
- case 'warn':
- colorCode = colors.yellow;
- prefix = 'WARN';
- break;
- case 'success':
- colorCode = colors.green;
- prefix = 'SUCCESS';
- break;
- case 'info':
- colorCode = colors.cyan;
- prefix = 'INFO';
- break;
- case 'debug':
- colorCode = colors.magenta;
- prefix = 'DEBUG';
- break;
- }
-
- console.log(`${colors.bright}[${prefix}]${colors.reset} ${colorCode}${message}${colors.reset}`);
-}
-
-function displayBanner() {
- const banner = `
-${colors.cyan}╔═══════════════════════════════════════════════════════════════╗
-║ ║
-║ ${colors.bright}DATABASE DEBUGGING TOOL${colors.reset}${colors.cyan} ║
-║ ${colors.white}Comprehensive Database Diagnostics${colors.reset}${colors.cyan} ║
-║ ║
-╚═══════════════════════════════════════════════════════════════╝${colors.reset}
-`;
- console.log(banner);
-}
-
-async function runComprehensiveCheck() {
- try {
- displayBanner();
-
- log('info', 'Starting comprehensive database diagnostics...');
-
- const validator = new DatabaseValidator();
- const results = await validator.validateDatabase();
-
- // Display results in organized sections
- console.log('\n' + colors.bright + '='.repeat(60) + colors.reset);
- console.log(colors.bright + 'DATABASE VALIDATION RESULTS' + colors.reset);
- console.log(colors.bright + '='.repeat(60) + colors.reset);
-
- // Overall Status
- const overallStatus = results.success ?
- `${colors.green}✅ PASSED${colors.reset}` :
- `${colors.red}❌ FAILED${colors.reset}`;
- console.log(`\nOverall Status: ${overallStatus}`);
- console.log(`Validation Duration: ${results.duration}ms\n`);
-
- // Connectivity Check
- console.log(colors.cyan + '📡 CONNECTIVITY CHECK' + colors.reset);
- if (results.connectivity?.success) {
- log('success', 'Database connection established');
- console.log(` Database: ${results.connectivity.database}`);
- console.log(` Host: ${results.connectivity.host}:${results.connectivity.port}`);
- console.log(` Version: ${results.connectivity.version}`);
- console.log(` Size: ${results.connectivity.size}`);
- console.log(` SSL: ${results.connectivity.ssl}`);
- console.log(` Pool: ${results.connectivity.pool.used}/${results.connectivity.pool.max} connections used`);
- } else {
- log('error', `Connection failed: ${results.connectivity?.error}`);
- if (results.connectivity?.troubleshooting) {
- console.log(colors.yellow + ' Troubleshooting tips:' + colors.reset);
- results.connectivity.troubleshooting.forEach(tip =>
- console.log(` - ${tip}`)
- );
- }
- }
-
- // Migration Check
- console.log('\n' + colors.cyan + '📦 MIGRATION STATUS' + colors.reset);
- if (results.migrations?.success) {
- log('success', 'All migrations are up to date');
- console.log(` Current Batch: ${results.migrations.currentBatch}`);
- console.log(` Completed: ${results.migrations.completed} migrations`);
- console.log(` Pending: ${results.migrations.pending} migrations`);
-
- if (results.migrations.autoRan) {
- log('info', `Auto-ran ${results.migrations.autoRan} pending migrations`);
- }
- } else {
- log('error', `Migration check failed: ${results.migrations?.error}`);
- }
-
- // Schema Check
- console.log('\n' + colors.cyan + '🗂️ SCHEMA VALIDATION' + colors.reset);
- if (results.schema?.success) {
- log('success', 'All required tables exist');
- console.log(` Coverage: ${results.schema.coverage}`);
- console.log(` Total Tables: ${results.schema.totalTables}`);
-
- // Table details
- console.log('\n Table Details:');
- Object.entries(results.schema.tables).forEach(([tableName, info]) => {
- if (info.exists) {
- console.log(` ✅ ${tableName} (${info.columns} columns, ${info.rows} rows)`);
- } else {
- console.log(` ❌ ${tableName} - ${info.error}`);
- }
- });
-
- // Optional tables if available
- if (results.schema.optionalTables) {
- console.log('\n Optional Tables:');
- Object.entries(results.schema.optionalTables).forEach(([tableName, info]) => {
- console.log(` 📦 ${tableName} (${info.columns} columns, ${info.rows} rows)`);
- });
- }
- } else {
- log('error', 'Schema validation failed');
- if (results.schema?.missingTables?.length > 0) {
- console.log(` Missing tables: ${results.schema.missingTables.join(', ')}`);
- }
- if (results.schema?.troubleshooting) {
- console.log(colors.yellow + ' Troubleshooting tips:' + colors.reset);
- results.schema.troubleshooting.forEach(tip =>
- console.log(` - ${tip}`)
- );
- }
- }
-
- // Seed Data Check
- console.log('\n' + colors.cyan + '🌱 SEED DATA STATUS' + colors.reset);
- if (results.seeds?.success) {
- log('success', results.seeds.message);
-
- if (results.seeds.autoSeeded) {
- log('info', 'Seed data was automatically populated');
- }
-
- Object.entries(results.seeds.checks).forEach(([checkName, check]) => {
- if (check.seeded) {
- console.log(` ✅ ${checkName}: ${check.count || 'OK'}`);
- } else {
- console.log(` ❌ ${checkName}: ${check.error || 'Not seeded'}`);
- }
- });
- } else {
- log('error', `Seed data check failed: ${results.seeds?.error}`);
- }
-
- // Integrity Check
- console.log('\n' + colors.cyan + '🔒 DATA INTEGRITY' + colors.reset);
- if (results.integrity?.success) {
- log('success', results.integrity.summary);
-
- results.integrity.checks.forEach(check => {
- if (check.passed) {
- console.log(` ✅ ${check.name}: ${check.message}`);
- } else {
- console.log(` ❌ ${check.name}: ${check.error || 'Failed'}`);
- if (check.violations?.length > 0) {
- check.violations.forEach(violation =>
- console.log(` - ${violation}`)
- );
- }
- }
- });
- } else {
- log('error', `Integrity check failed: ${results.integrity?.error}`);
- }
-
- // Final Summary
- console.log('\n' + colors.bright + '='.repeat(60) + colors.reset);
- console.log(colors.bright + 'DEBUGGING SUMMARY' + colors.reset);
- console.log(colors.bright + '='.repeat(60) + colors.reset);
-
- if (results.success) {
- log('success', '🎉 All database checks passed! Your database is ready.');
- } else {
- log('error', '❌ Database validation failed. Please review the issues above.');
-
- // Provide actionable steps
- console.log('\n' + colors.yellow + 'Recommended Actions:' + colors.reset);
-
- if (!results.connectivity?.success) {
- console.log('1. Fix database connectivity issues first');
- }
-
- if (!results.migrations?.success) {
- console.log('2. Run database migrations: npm run db:migrate');
- }
-
- if (!results.schema?.success) {
- console.log('3. Ensure all required tables exist by running migrations');
- }
-
- if (!results.seeds?.success) {
- console.log('4. Populate seed data: npm run db:seed');
- }
-
- if (!results.integrity?.success) {
- console.log('5. Review and fix data integrity issues');
- }
- }
-
- console.log('');
-
- } catch (error) {
- log('error', `Debugging tool failed: ${error.message}`);
- console.error(error.stack);
- process.exit(1);
- }
-}
-
-// Command line interface
-const command = process.argv[2];
-
-switch (command) {
- case 'check':
- case undefined:
- runComprehensiveCheck();
- break;
- case 'help':
- console.log(`
-Database Debugging Tool
-
-Usage:
- node scripts/debug-database.js [command]
-
-Commands:
- check (default) Run comprehensive database diagnostics
- help Show this help message
-
-Examples:
- node scripts/debug-database.js
- node scripts/debug-database.js check
-`);
- break;
- default:
- log('error', `Unknown command: ${command}`);
- log('info', 'Use "help" for available commands');
- process.exit(1);
-}
diff --git a/scripts/health-monitor.js b/scripts/health-monitor.js
deleted file mode 100644
index 34f4d00..0000000
--- a/scripts/health-monitor.js
+++ /dev/null
@@ -1,506 +0,0 @@
-/**
- * Shattered Void MMO - Health Monitoring System
- *
- * This module provides comprehensive health monitoring for all game services,
- * including real-time status checks, performance metrics, and alerting.
- */
-
-const http = require('http');
-const { EventEmitter } = require('events');
-const os = require('os');
-
-class HealthMonitor extends EventEmitter {
- constructor(options = {}) {
- super();
-
- this.services = options.services || {};
- this.interval = options.interval || 30000; // 30 seconds
- this.onHealthChange = options.onHealthChange || null;
- this.timeout = options.timeout || 5000; // 5 seconds
-
- this.healthStatus = {};
- this.metrics = {};
- this.alertThresholds = {
- responseTime: 5000, // 5 seconds
- memoryUsage: 80, // 80%
- cpuUsage: 90, // 90%
- errorRate: 10 // 10%
- };
-
- this.monitoringInterval = null;
- this.isRunning = false;
- this.healthHistory = {};
-
- // Initialize health status for all services
- this.initializeHealthStatus();
- }
-
- /**
- * Initialize health status tracking
- */
- initializeHealthStatus() {
- Object.keys(this.services).forEach(serviceName => {
- this.healthStatus[serviceName] = {
- status: 'unknown',
- lastCheck: null,
- responseTime: null,
- consecutiveFailures: 0,
- uptime: 0,
- lastError: null
- };
-
- this.healthHistory[serviceName] = [];
- });
- }
-
- /**
- * Start health monitoring
- */
- async start() {
- if (this.isRunning) {
- throw new Error('Health monitor is already running');
- }
-
- this.isRunning = true;
- console.log(`🏥 Health monitoring started (interval: ${this.interval}ms)`);
-
- // Initial health check
- await this.performHealthChecks();
-
- // Start periodic monitoring
- this.monitoringInterval = setInterval(async () => {
- try {
- await this.performHealthChecks();
- } catch (error) {
- console.error('Health check error:', error);
- }
- }, this.interval);
-
- // Start system metrics monitoring
- this.startSystemMetricsMonitoring();
-
- this.emit('started');
- }
-
- /**
- * Stop health monitoring
- */
- stop() {
- if (!this.isRunning) {
- return;
- }
-
- this.isRunning = false;
-
- if (this.monitoringInterval) {
- clearInterval(this.monitoringInterval);
- this.monitoringInterval = null;
- }
-
- console.log('🏥 Health monitoring stopped');
- this.emit('stopped');
- }
-
- /**
- * Perform health checks on all services
- */
- async performHealthChecks() {
- const checkPromises = Object.entries(this.services).map(([serviceName, serviceInfo]) => {
- return this.checkServiceHealth(serviceName, serviceInfo);
- });
-
- await Promise.allSettled(checkPromises);
- this.updateHealthSummary();
- }
-
- /**
- * Check health of a specific service
- */
- async checkServiceHealth(serviceName, serviceInfo) {
- const startTime = Date.now();
- const previousStatus = this.healthStatus[serviceName].status;
-
- try {
- let isHealthy = false;
- let responseTime = null;
-
- // Different health check strategies based on service type
- switch (serviceName) {
- case 'backend':
- isHealthy = await this.checkHttpService(serviceInfo.port, '/health');
- responseTime = Date.now() - startTime;
- break;
-
- case 'frontend':
- isHealthy = await this.checkHttpService(serviceInfo.port);
- responseTime = Date.now() - startTime;
- break;
-
- case 'database':
- isHealthy = await this.checkDatabaseHealth();
- responseTime = Date.now() - startTime;
- break;
-
- case 'redis':
- isHealthy = await this.checkRedisHealth();
- responseTime = Date.now() - startTime;
- break;
-
- default:
- // For other services, assume healthy if they exist
- isHealthy = true;
- responseTime = Date.now() - startTime;
- }
-
- // Update health status
- const newStatus = isHealthy ? 'healthy' : 'unhealthy';
- this.updateServiceStatus(serviceName, {
- status: newStatus,
- lastCheck: new Date(),
- responseTime,
- consecutiveFailures: isHealthy ? 0 : this.healthStatus[serviceName].consecutiveFailures + 1,
- lastError: null
- });
-
- // Emit health change event if status changed
- if (previousStatus !== newStatus && this.onHealthChange) {
- this.onHealthChange(serviceName, newStatus);
- }
-
- } catch (error) {
- const responseTime = Date.now() - startTime;
-
- this.updateServiceStatus(serviceName, {
- status: 'unhealthy',
- lastCheck: new Date(),
- responseTime,
- consecutiveFailures: this.healthStatus[serviceName].consecutiveFailures + 1,
- lastError: error.message
- });
-
- // Emit health change event if status changed
- if (previousStatus !== 'unhealthy' && this.onHealthChange) {
- this.onHealthChange(serviceName, 'unhealthy');
- }
-
- console.error(`Health check failed for ${serviceName}:`, error.message);
- }
- }
-
- /**
- * Check HTTP service health
- */
- checkHttpService(port, path = '/') {
- return new Promise((resolve, reject) => {
- const options = {
- hostname: 'localhost',
- port: port,
- path: path,
- method: 'GET',
- timeout: this.timeout
- };
-
- const req = http.request(options, (res) => {
- // Consider 2xx and 3xx status codes as healthy
- resolve(res.statusCode >= 200 && res.statusCode < 400);
- });
-
- req.on('error', (error) => {
- reject(error);
- });
-
- req.on('timeout', () => {
- req.destroy();
- reject(new Error('Request timeout'));
- });
-
- req.end();
- });
- }
-
- /**
- * Check database health
- */
- async checkDatabaseHealth() {
- try {
- // Try to get database connection from the app
- const db = require('../src/database/connection');
-
- // Simple query to check database connectivity
- await db.raw('SELECT 1');
- return true;
- } catch (error) {
- return false;
- }
- }
-
- /**
- * Check Redis health
- */
- async checkRedisHealth() {
- try {
- // Skip if Redis is disabled
- if (process.env.DISABLE_REDIS === 'true') {
- return true;
- }
-
- // Try to get Redis client from the app
- const redisConfig = require('../src/config/redis');
-
- if (!redisConfig.client) {
- return false;
- }
-
- // Simple ping to check Redis connectivity
- await redisConfig.client.ping();
- return true;
- } catch (error) {
- return false;
- }
- }
-
- /**
- * Update service status
- */
- updateServiceStatus(serviceName, statusUpdate) {
- this.healthStatus[serviceName] = {
- ...this.healthStatus[serviceName],
- ...statusUpdate
- };
-
- // Add to health history
- this.addToHealthHistory(serviceName, statusUpdate);
-
- // Check for alerts
- this.checkForAlerts(serviceName);
- }
-
- /**
- * Add health data to history
- */
- addToHealthHistory(serviceName, statusData) {
- const historyEntry = {
- timestamp: Date.now(),
- status: statusData.status,
- responseTime: statusData.responseTime,
- error: statusData.lastError
- };
-
- this.healthHistory[serviceName].push(historyEntry);
-
- // Keep only last 100 entries
- if (this.healthHistory[serviceName].length > 100) {
- this.healthHistory[serviceName] = this.healthHistory[serviceName].slice(-100);
- }
- }
-
- /**
- * Check for health alerts
- */
- checkForAlerts(serviceName) {
- const health = this.healthStatus[serviceName];
- const alerts = [];
-
- // Check consecutive failures
- if (health.consecutiveFailures >= 3) {
- alerts.push({
- type: 'consecutive_failures',
- message: `Service ${serviceName} has failed ${health.consecutiveFailures} consecutive times`,
- severity: 'critical'
- });
- }
-
- // Check response time
- if (health.responseTime && health.responseTime > this.alertThresholds.responseTime) {
- alerts.push({
- type: 'slow_response',
- message: `Service ${serviceName} response time: ${health.responseTime}ms (threshold: ${this.alertThresholds.responseTime}ms)`,
- severity: 'warning'
- });
- }
-
- // Emit alerts
- alerts.forEach(alert => {
- this.emit('alert', serviceName, alert);
- });
- }
-
- /**
- * Start system metrics monitoring
- */
- startSystemMetricsMonitoring() {
- const updateSystemMetrics = () => {
- const memUsage = process.memoryUsage();
- const cpuUsage = process.cpuUsage();
- const systemMem = {
- total: os.totalmem(),
- free: os.freemem()
- };
-
- this.metrics.system = {
- timestamp: Date.now(),
- memory: {
- rss: memUsage.rss,
- heapTotal: memUsage.heapTotal,
- heapUsed: memUsage.heapUsed,
- external: memUsage.external,
- usage: Math.round((memUsage.heapUsed / memUsage.heapTotal) * 100)
- },
- cpu: {
- user: cpuUsage.user,
- system: cpuUsage.system
- },
- systemMemory: {
- total: systemMem.total,
- free: systemMem.free,
- used: systemMem.total - systemMem.free,
- usage: Math.round(((systemMem.total - systemMem.free) / systemMem.total) * 100)
- },
- uptime: process.uptime(),
- loadAverage: os.loadavg()
- };
-
- // Check for system alerts
- this.checkSystemAlerts();
- };
-
- // Update immediately
- updateSystemMetrics();
-
- // Update every 10 seconds
- setInterval(updateSystemMetrics, 10000);
- }
-
- /**
- * Check for system-level alerts
- */
- checkSystemAlerts() {
- const metrics = this.metrics.system;
-
- if (!metrics) return;
-
- // Memory usage alert
- if (metrics.memory.usage > this.alertThresholds.memoryUsage) {
- this.emit('alert', 'system', {
- type: 'high_memory_usage',
- message: `High memory usage: ${metrics.memory.usage}% (threshold: ${this.alertThresholds.memoryUsage}%)`,
- severity: 'warning'
- });
- }
-
- // System memory alert
- if (metrics.systemMemory.usage > this.alertThresholds.memoryUsage) {
- this.emit('alert', 'system', {
- type: 'high_system_memory',
- message: `High system memory usage: ${metrics.systemMemory.usage}% (threshold: ${this.alertThresholds.memoryUsage}%)`,
- severity: 'critical'
- });
- }
- }
-
- /**
- * Update overall health summary
- */
- updateHealthSummary() {
- const services = Object.keys(this.healthStatus);
- const healthyServices = services.filter(s => this.healthStatus[s].status === 'healthy');
- const unhealthyServices = services.filter(s => this.healthStatus[s].status === 'unhealthy');
-
- this.metrics.summary = {
- timestamp: Date.now(),
- totalServices: services.length,
- healthyServices: healthyServices.length,
- unhealthyServices: unhealthyServices.length,
- overallHealth: unhealthyServices.length === 0 ? 'healthy' : 'degraded'
- };
- }
-
- /**
- * Get current health status
- */
- getHealthStatus() {
- return {
- services: this.healthStatus,
- metrics: this.metrics,
- summary: this.metrics.summary,
- isRunning: this.isRunning
- };
- }
-
- /**
- * Get health history for a service
- */
- getHealthHistory(serviceName) {
- return this.healthHistory[serviceName] || [];
- }
-
- /**
- * Get service uptime
- */
- getServiceUptime(serviceName) {
- const history = this.healthHistory[serviceName];
- if (!history || history.length === 0) {
- return 0;
- }
-
- const now = Date.now();
- const oneDayAgo = now - (24 * 60 * 60 * 1000);
-
- const recentHistory = history.filter(entry => entry.timestamp > oneDayAgo);
-
- if (recentHistory.length === 0) {
- return 0;
- }
-
- const healthyCount = recentHistory.filter(entry => entry.status === 'healthy').length;
- return Math.round((healthyCount / recentHistory.length) * 100);
- }
-
- /**
- * Generate health report
- */
- generateHealthReport() {
- const services = Object.keys(this.healthStatus);
- const report = {
- timestamp: new Date().toISOString(),
- summary: this.metrics.summary,
- services: {},
- systemMetrics: this.metrics.system,
- alerts: []
- };
-
- services.forEach(serviceName => {
- const health = this.healthStatus[serviceName];
- const uptime = this.getServiceUptime(serviceName);
-
- report.services[serviceName] = {
- status: health.status,
- lastCheck: health.lastCheck,
- responseTime: health.responseTime,
- consecutiveFailures: health.consecutiveFailures,
- uptime: `${uptime}%`,
- lastError: health.lastError
- };
- });
-
- return report;
- }
-
- /**
- * Export health data for monitoring systems
- */
- exportMetrics() {
- return {
- timestamp: Date.now(),
- services: this.healthStatus,
- system: this.metrics.system,
- summary: this.metrics.summary,
- uptime: Object.keys(this.healthStatus).reduce((acc, serviceName) => {
- acc[serviceName] = this.getServiceUptime(serviceName);
- return acc;
- }, {})
- };
- }
-}
-
-module.exports = HealthMonitor;
\ No newline at end of file
diff --git a/scripts/setup-combat.js b/scripts/setup-combat.js
deleted file mode 100644
index 7a8fc99..0000000
--- a/scripts/setup-combat.js
+++ /dev/null
@@ -1,314 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * Combat System Setup Script
- * Initializes combat configurations and sample data
- */
-
-const db = require('../src/database/connection');
-const logger = require('../src/utils/logger');
-
-async function setupCombatSystem() {
- try {
- console.log('🚀 Setting up combat system...');
-
- // Insert default combat configurations
- console.log('📝 Adding default combat configurations...');
-
- const existingConfigs = await db('combat_configurations').select('id');
- if (existingConfigs.length === 0) {
- await db('combat_configurations').insert([
- {
- config_name: 'instant_combat',
- combat_type: 'instant',
- config_data: JSON.stringify({
- auto_resolve: true,
- preparation_time: 5,
- damage_variance: 0.15,
- experience_gain: 1.0,
- casualty_rate_min: 0.05,
- casualty_rate_max: 0.75,
- loot_multiplier: 1.0,
- spectator_limit: 50,
- priority: 100
- }),
- description: 'Standard instant combat resolution with quick results',
- is_active: true,
- created_at: new Date(),
- updated_at: new Date()
- },
- {
- config_name: 'turn_based_combat',
- combat_type: 'turn_based',
- config_data: JSON.stringify({
- auto_resolve: true,
- preparation_time: 10,
- max_rounds: 15,
- round_duration: 3,
- damage_variance: 0.2,
- experience_gain: 1.5,
- casualty_rate_min: 0.1,
- casualty_rate_max: 0.8,
- loot_multiplier: 1.2,
- spectator_limit: 100,
- priority: 150
- }),
- description: 'Detailed turn-based combat with round-by-round resolution',
- is_active: true,
- created_at: new Date(),
- updated_at: new Date()
- },
- {
- config_name: 'tactical_combat',
- combat_type: 'tactical',
- config_data: JSON.stringify({
- auto_resolve: true,
- preparation_time: 15,
- max_rounds: 20,
- round_duration: 4,
- damage_variance: 0.25,
- experience_gain: 2.0,
- casualty_rate_min: 0.15,
- casualty_rate_max: 0.85,
- loot_multiplier: 1.5,
- spectator_limit: 200,
- priority: 200
- }),
- description: 'Advanced tactical combat with positioning and formations',
- is_active: true,
- created_at: new Date(),
- updated_at: new Date()
- }
- ]);
-
- console.log('✅ Combat configurations added successfully');
- } else {
- console.log('ℹ️ Combat configurations already exist, skipping...');
- }
-
- // Update combat types table with default plugin reference
- console.log('📝 Updating combat types...');
-
- const existingCombatTypes = await db('combat_types').select('id');
- if (existingCombatTypes.length === 0) {
- await db('combat_types').insert([
- {
- name: 'instant_resolution',
- description: 'Basic instant combat resolution with detailed logs',
- plugin_name: 'instant_combat',
- config: JSON.stringify({
- calculate_experience: true,
- detailed_logs: true,
- enable_spectators: true
- }),
- is_active: true
- },
- {
- name: 'turn_based_resolution',
- description: 'Turn-based combat with round-by-round progression',
- plugin_name: 'turn_based_combat',
- config: JSON.stringify({
- calculate_experience: true,
- detailed_logs: true,
- enable_spectators: true,
- show_round_details: true
- }),
- is_active: true
- },
- {
- name: 'tactical_resolution',
- description: 'Advanced tactical combat with formations and positioning',
- plugin_name: 'tactical_combat',
- config: JSON.stringify({
- calculate_experience: true,
- detailed_logs: true,
- enable_spectators: true,
- enable_formations: true,
- enable_positioning: true
- }),
- is_active: true
- }
- ]);
-
- console.log('✅ Combat types added successfully');
- } else {
- console.log('ℹ️ Combat types already exist, skipping...');
- }
-
- // Ensure combat plugins are properly registered
- console.log('📝 Checking combat plugins...');
-
- const combatPlugins = await db('plugins').where('plugin_type', 'combat');
- const pluginNames = combatPlugins.map(p => p.name);
-
- const requiredPlugins = [
- {
- name: 'instant_combat',
- version: '1.0.0',
- description: 'Basic instant combat resolution system',
- plugin_type: 'combat',
- is_active: true,
- config: JSON.stringify({
- damage_variance: 0.15,
- experience_gain: 1.0
- }),
- dependencies: JSON.stringify([]),
- hooks: JSON.stringify(['pre_combat', 'post_combat', 'damage_calculation'])
- },
- {
- name: 'turn_based_combat',
- version: '1.0.0',
- description: 'Turn-based combat resolution system with detailed rounds',
- plugin_type: 'combat',
- is_active: true,
- config: JSON.stringify({
- max_rounds: 15,
- damage_variance: 0.2,
- experience_gain: 1.5
- }),
- dependencies: JSON.stringify([]),
- hooks: JSON.stringify(['pre_combat', 'post_combat', 'round_start', 'round_end', 'damage_calculation'])
- },
- {
- name: 'tactical_combat',
- version: '1.0.0',
- description: 'Advanced tactical combat with formations and positioning',
- plugin_type: 'combat',
- is_active: true,
- config: JSON.stringify({
- enable_formations: true,
- enable_positioning: true,
- damage_variance: 0.25,
- experience_gain: 2.0
- }),
- dependencies: JSON.stringify([]),
- hooks: JSON.stringify(['pre_combat', 'post_combat', 'formation_change', 'position_update', 'damage_calculation'])
- }
- ];
-
- for (const plugin of requiredPlugins) {
- if (!pluginNames.includes(plugin.name)) {
- await db('plugins').insert(plugin);
- console.log(`✅ Added combat plugin: ${plugin.name}`);
- } else {
- console.log(`ℹ️ Combat plugin ${plugin.name} already exists`);
- }
- }
-
- // Add sample ship designs if none exist (for testing)
- console.log('📝 Checking for sample ship designs...');
-
- const existingDesigns = await db('ship_designs').where('is_public', true);
- if (existingDesigns.length === 0) {
- await db('ship_designs').insert([
- {
- name: 'Basic Fighter',
- ship_class: 'fighter',
- hull_type: 'light',
- components: JSON.stringify({
- weapons: ['laser_cannon'],
- shields: ['basic_shield'],
- engines: ['ion_drive']
- }),
- stats: JSON.stringify({
- hp: 75,
- attack: 12,
- defense: 8,
- speed: 6
- }),
- cost: JSON.stringify({
- scrap: 80,
- energy: 40
- }),
- build_time: 20,
- is_public: true,
- is_active: true,
- hull_points: 75,
- shield_points: 20,
- armor_points: 5,
- attack_power: 12,
- attack_speed: 1.2,
- movement_speed: 6,
- cargo_capacity: 0,
- special_abilities: JSON.stringify([]),
- damage_resistances: JSON.stringify({}),
- created_at: new Date(),
- updated_at: new Date()
- },
- {
- name: 'Heavy Cruiser',
- ship_class: 'cruiser',
- hull_type: 'heavy',
- components: JSON.stringify({
- weapons: ['plasma_cannon', 'missile_launcher'],
- shields: ['reinforced_shield'],
- engines: ['fusion_drive']
- }),
- stats: JSON.stringify({
- hp: 200,
- attack: 25,
- defense: 18,
- speed: 3
- }),
- cost: JSON.stringify({
- scrap: 300,
- energy: 180,
- rare_elements: 5
- }),
- build_time: 120,
- is_public: true,
- is_active: true,
- hull_points: 200,
- shield_points: 60,
- armor_points: 25,
- attack_power: 25,
- attack_speed: 0.8,
- movement_speed: 3,
- cargo_capacity: 50,
- special_abilities: JSON.stringify(['heavy_armor', 'shield_boost']),
- damage_resistances: JSON.stringify({
- kinetic: 0.1,
- energy: 0.05
- }),
- created_at: new Date(),
- updated_at: new Date()
- }
- ]);
-
- console.log('✅ Added sample ship designs');
- } else {
- console.log('ℹ️ Ship designs already exist, skipping...');
- }
-
- console.log('🎉 Combat system setup completed successfully!');
- console.log('');
- console.log('Combat system is now ready for use with:');
- console.log('- 3 combat configurations (instant, turn-based, tactical)');
- console.log('- 3 combat resolution plugins');
- console.log('- Sample ship designs for testing');
- console.log('');
- console.log('You can now:');
- console.log('• Create fleets and initiate combat via /api/combat/initiate');
- console.log('• View combat history via /api/combat/history');
- console.log('• Manage combat system via admin endpoints');
-
- } catch (error) {
- console.error('❌ Combat system setup failed:', error);
- throw error;
- }
-}
-
-// Main execution
-if (require.main === module) {
- setupCombatSystem()
- .then(() => {
- console.log('✨ Setup completed successfully');
- process.exit(0);
- })
- .catch(error => {
- console.error('💥 Setup failed:', error);
- process.exit(1);
- });
-}
-
-module.exports = { setupCombatSystem };
\ No newline at end of file
diff --git a/scripts/startup-checks.js b/scripts/startup-checks.js
deleted file mode 100644
index f3fe607..0000000
--- a/scripts/startup-checks.js
+++ /dev/null
@@ -1,591 +0,0 @@
-/**
- * Shattered Void MMO - Comprehensive Startup Checks
- *
- * This module performs thorough pre-flight checks to ensure all dependencies,
- * configurations, and system requirements are met before starting the game.
- */
-
-const fs = require('fs').promises;
-const path = require('path');
-const { exec } = require('child_process');
-const { promisify } = require('util');
-const net = require('net');
-
-const execAsync = promisify(exec);
-
-class StartupChecks {
- constructor() {
- this.checks = [];
- this.results = {};
- }
-
- /**
- * Add a check to the validation suite
- */
- addCheck(name, checkFunction, required = true) {
- this.checks.push({
- name,
- function: checkFunction,
- required
- });
- }
-
- /**
- * Run all registered checks
- */
- async runAllChecks() {
- const startTime = Date.now();
- const results = {
- success: true,
- checks: {},
- failures: [],
- duration: 0
- };
-
- // Register all standard checks
- this.registerStandardChecks();
-
- console.log(`🔍 Running ${this.checks.length} startup checks...`);
-
- for (const check of this.checks) {
- try {
- console.log(` ⏳ ${check.name}...`);
- const checkResult = await check.function();
-
- results.checks[check.name] = {
- success: true,
- required: check.required,
- details: checkResult
- };
-
- console.log(` ✅ ${check.name}`);
- } catch (error) {
- const failure = {
- name: check.name,
- required: check.required,
- error: error.message
- };
-
- results.checks[check.name] = {
- success: false,
- required: check.required,
- error: error.message
- };
-
- results.failures.push(failure);
-
- if (check.required) {
- results.success = false;
- console.log(` ❌ ${check.name}: ${error.message}`);
- } else {
- console.log(` ⚠️ ${check.name}: ${error.message} (optional)`);
- }
- }
- }
-
- results.duration = Date.now() - startTime;
- return results;
- }
-
- /**
- * Register all standard checks
- */
- registerStandardChecks() {
- // Node.js version check
- this.addCheck('Node.js Version', this.checkNodeVersion, true);
-
- // NPM availability
- this.addCheck('NPM Availability', this.checkNpmAvailability, true);
-
- // Environment configuration
- this.addCheck('Environment Configuration', this.checkEnvironmentConfig, true);
-
- // Required directories
- this.addCheck('Directory Structure', this.checkDirectoryStructure, true);
-
- // Package dependencies
- this.addCheck('Package Dependencies', this.checkPackageDependencies, true);
-
- // Port availability
- this.addCheck('Port Availability', this.checkPortAvailability, true);
-
- // Database configuration
- this.addCheck('Database Configuration', this.checkDatabaseConfig, true);
-
- // Redis configuration
- this.addCheck('Redis Configuration', this.checkRedisConfig, false);
-
- // Log directories
- this.addCheck('Log Directories', this.checkLogDirectories, true);
-
- // Frontend availability
- this.addCheck('Frontend Dependencies', this.checkFrontendDependencies, false);
-
- // Memory availability
- this.addCheck('System Memory', this.checkSystemMemory, true);
-
- // Disk space
- this.addCheck('Disk Space', this.checkDiskSpace, true);
-
- // File permissions
- this.addCheck('File Permissions', this.checkFilePermissions, true);
- }
-
- /**
- * Check Node.js version requirements
- */
- async checkNodeVersion() {
- const requiredMajor = 18;
- const currentVersion = process.version;
- const major = parseInt(currentVersion.slice(1).split('.')[0]);
-
- if (major < requiredMajor) {
- throw new Error(`Node.js ${requiredMajor}+ required, found ${currentVersion}`);
- }
-
- return {
- current: currentVersion,
- required: `>=${requiredMajor}.0.0`,
- valid: true
- };
- }
-
- /**
- * Check NPM availability
- */
- async checkNpmAvailability() {
- try {
- const { stdout } = await execAsync('npm --version');
- const version = stdout.trim();
-
- return {
- version,
- available: true
- };
- } catch (error) {
- throw new Error('NPM not found in PATH');
- }
- }
-
- /**
- * Check environment configuration
- */
- async checkEnvironmentConfig() {
- const envFile = path.join(process.cwd(), '.env');
- const config = {
- hasEnvFile: false,
- requiredVars: [],
- missingVars: [],
- warnings: []
- };
-
- // Check for .env file
- try {
- await fs.access(envFile);
- config.hasEnvFile = true;
- } catch {
- config.warnings.push('No .env file found, using defaults');
- }
-
- // Required environment variables (with defaults)
- const requiredVars = [
- { name: 'NODE_ENV', default: 'development' },
- { name: 'PORT', default: '3000' },
- { name: 'DB_HOST', default: 'localhost' },
- { name: 'DB_PORT', default: '5432' },
- { name: 'DB_NAME', default: 'shattered_void_dev' },
- { name: 'DB_USER', default: 'postgres' }
- ];
-
- for (const varConfig of requiredVars) {
- const value = process.env[varConfig.name];
- if (!value) {
- config.missingVars.push({
- name: varConfig.name,
- default: varConfig.default
- });
- } else {
- config.requiredVars.push({
- name: varConfig.name,
- value: varConfig.name.includes('PASSWORD') ? '[HIDDEN]' : value
- });
- }
- }
-
- return config;
- }
-
- /**
- * Check directory structure
- */
- async checkDirectoryStructure() {
- const requiredDirs = [
- 'src',
- 'src/controllers',
- 'src/services',
- 'src/routes',
- 'src/database',
- 'src/database/migrations',
- 'config',
- 'scripts'
- ];
-
- const optionalDirs = [
- 'frontend',
- 'frontend/src',
- 'frontend/dist',
- 'logs',
- 'tests'
- ];
-
- const results = {
- required: [],
- optional: [],
- missing: []
- };
-
- // Check required directories
- for (const dir of requiredDirs) {
- try {
- const stats = await fs.stat(dir);
- if (stats.isDirectory()) {
- results.required.push(dir);
- } else {
- results.missing.push(dir);
- }
- } catch {
- results.missing.push(dir);
- }
- }
-
- // Check optional directories
- for (const dir of optionalDirs) {
- try {
- const stats = await fs.stat(dir);
- if (stats.isDirectory()) {
- results.optional.push(dir);
- }
- } catch {
- // Optional directories are not reported as missing
- }
- }
-
- if (results.missing.length > 0) {
- throw new Error(`Missing required directories: ${results.missing.join(', ')}`);
- }
-
- return results;
- }
-
- /**
- * Check package dependencies
- */
- async checkPackageDependencies() {
- const packageJsonPath = path.join(process.cwd(), 'package.json');
- const nodeModulesPath = path.join(process.cwd(), 'node_modules');
-
- try {
- // Check package.json exists
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
-
- // Check node_modules exists
- await fs.access(nodeModulesPath);
-
- // Check critical dependencies
- const criticalDeps = [
- 'express',
- 'pg',
- 'knex',
- 'winston',
- 'dotenv',
- 'socket.io'
- ];
-
- const missing = [];
- for (const dep of criticalDeps) {
- try {
- await fs.access(path.join(nodeModulesPath, dep));
- } catch {
- missing.push(dep);
- }
- }
-
- if (missing.length > 0) {
- throw new Error(`Missing critical dependencies: ${missing.join(', ')}`);
- }
-
- return {
- packageJson: packageJson.name,
- version: packageJson.version,
- dependencies: Object.keys(packageJson.dependencies || {}).length,
- devDependencies: Object.keys(packageJson.devDependencies || {}).length,
- criticalDeps: criticalDeps.length
- };
- } catch (error) {
- throw new Error(`Package validation failed: ${error.message}`);
- }
- }
-
- /**
- * Check port availability
- */
- async checkPortAvailability() {
- const backendPort = process.env.PORT || 3000;
- const frontendPort = process.env.FRONTEND_PORT || 5173;
-
- const checkPort = (port) => {
- return new Promise((resolve, reject) => {
- const server = net.createServer();
-
- server.listen(port, (err) => {
- if (err) {
- reject(new Error(`Port ${port} is in use`));
- } else {
- server.close(() => resolve(port));
- }
- });
-
- server.on('error', (err) => {
- reject(new Error(`Port ${port} is in use`));
- });
- });
- };
-
- const results = {
- backend: await checkPort(backendPort),
- frontend: null
- };
-
- // Only check frontend port if frontend is enabled
- if (process.env.ENABLE_FRONTEND !== 'false') {
- try {
- results.frontend = await checkPort(frontendPort);
- } catch (error) {
- // Frontend port check is not critical
- results.frontendError = error.message;
- }
- }
-
- return results;
- }
-
- /**
- * Check database configuration
- */
- async checkDatabaseConfig() {
- const config = {
- host: process.env.DB_HOST || 'localhost',
- port: process.env.DB_PORT || 5432,
- database: process.env.DB_NAME || 'shattered_void_dev',
- user: process.env.DB_USER || 'postgres'
- };
-
- // Check if database connection parameters are reasonable
- if (!config.host || !config.port || !config.database || !config.user) {
- throw new Error('Incomplete database configuration');
- }
-
- // Validate port number
- const port = parseInt(config.port);
- if (isNaN(port) || port < 1 || port > 65535) {
- throw new Error(`Invalid database port: ${config.port}`);
- }
-
- return {
- host: config.host,
- port: config.port,
- database: config.database,
- user: config.user,
- configured: true
- };
- }
-
- /**
- * Check Redis configuration (optional)
- */
- async checkRedisConfig() {
- const config = {
- host: process.env.REDIS_HOST || 'localhost',
- port: process.env.REDIS_PORT || 6379,
- enabled: process.env.DISABLE_REDIS !== 'true'
- };
-
- if (!config.enabled) {
- return {
- enabled: false,
- message: 'Redis disabled by configuration'
- };
- }
-
- // Validate port number
- const port = parseInt(config.port);
- if (isNaN(port) || port < 1 || port > 65535) {
- throw new Error(`Invalid Redis port: ${config.port}`);
- }
-
- return {
- host: config.host,
- port: config.port,
- enabled: true
- };
- }
-
- /**
- * Check log directories
- */
- async checkLogDirectories() {
- const logDir = path.join(process.cwd(), 'logs');
-
- try {
- // Check if logs directory exists
- await fs.access(logDir);
-
- // Check if it's writable
- await fs.access(logDir, fs.constants.W_OK);
-
- return {
- directory: logDir,
- exists: true,
- writable: true
- };
- } catch {
- // Create logs directory if it doesn't exist
- try {
- await fs.mkdir(logDir, { recursive: true });
- return {
- directory: logDir,
- exists: true,
- writable: true,
- created: true
- };
- } catch (error) {
- throw new Error(`Cannot create logs directory: ${error.message}`);
- }
- }
- }
-
- /**
- * Check frontend dependencies (optional)
- */
- async checkFrontendDependencies() {
- const frontendDir = path.join(process.cwd(), 'frontend');
-
- try {
- // Check if frontend directory exists
- await fs.access(frontendDir);
-
- // Check package.json
- const packageJsonPath = path.join(frontendDir, 'package.json');
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
-
- // Check node_modules
- const nodeModulesPath = path.join(frontendDir, 'node_modules');
- await fs.access(nodeModulesPath);
-
- return {
- directory: frontendDir,
- name: packageJson.name,
- version: packageJson.version,
- dependencies: Object.keys(packageJson.dependencies || {}).length,
- hasNodeModules: true
- };
- } catch (error) {
- throw new Error(`Frontend not available: ${error.message}`);
- }
- }
-
- /**
- * Check system memory
- */
- async checkSystemMemory() {
- const totalMemory = require('os').totalmem();
- const freeMemory = require('os').freemem();
- const usedMemory = totalMemory - freeMemory;
-
- const totalGB = totalMemory / (1024 * 1024 * 1024);
- const freeGB = freeMemory / (1024 * 1024 * 1024);
- const usedGB = usedMemory / (1024 * 1024 * 1024);
-
- // Minimum 1GB free memory recommended
- if (freeGB < 1) {
- throw new Error(`Low memory: ${freeGB.toFixed(2)}GB free, 1GB+ recommended`);
- }
-
- return {
- total: `${totalGB.toFixed(2)}GB`,
- used: `${usedGB.toFixed(2)}GB`,
- free: `${freeGB.toFixed(2)}GB`,
- usage: `${((usedGB / totalGB) * 100).toFixed(1)}%`
- };
- }
-
- /**
- * Check disk space
- */
- async checkDiskSpace() {
- try {
- const { stdout } = await execAsync('df -h .');
- const lines = stdout.trim().split('\n');
- const data = lines[1].split(/\s+/);
-
- const size = data[1];
- const used = data[2];
- const available = data[3];
- const usage = data[4];
-
- // Extract numeric percentage
- const usagePercent = parseInt(usage.replace('%', ''));
-
- // Warn if disk usage is over 90%
- if (usagePercent > 90) {
- throw new Error(`High disk usage: ${usage} used, <10% available`);
- }
-
- return {
- size,
- used,
- available,
- usage: `${usagePercent}%`
- };
- } catch (error) {
- // Fallback for non-Unix systems or when df is not available
- return {
- message: 'Disk space check not available on this system',
- available: true
- };
- }
- }
-
- /**
- * Check file permissions
- */
- async checkFilePermissions() {
- const criticalFiles = [
- 'src/server.js',
- 'package.json',
- 'knexfile.js'
- ];
-
- const results = {
- readable: [],
- unreadable: []
- };
-
- for (const file of criticalFiles) {
- try {
- await fs.access(file, fs.constants.R_OK);
- results.readable.push(file);
- } catch {
- results.unreadable.push(file);
- }
- }
-
- if (results.unreadable.length > 0) {
- throw new Error(`Cannot read critical files: ${results.unreadable.join(', ')}`);
- }
-
- return results;
- }
-}
-
-module.exports = StartupChecks;
\ No newline at end of file
diff --git a/src/app.js b/src/app.js
index 978c470..6d0d3b0 100644
--- a/src/app.js
+++ b/src/app.js
@@ -26,94 +26,94 @@ const routes = require('./routes');
* @returns {Object} Configured Express app
*/
function createApp() {
- const app = express();
- const NODE_ENV = process.env.NODE_ENV || 'development';
+ const app = express();
+ const NODE_ENV = process.env.NODE_ENV || 'development';
- // Add correlation ID to all requests for tracing
- app.use((req, res, next) => {
- req.correlationId = uuidv4();
- res.set('X-Correlation-ID', req.correlationId);
- next();
- });
-
- // Security middleware
- app.use(helmet({
- contentSecurityPolicy: NODE_ENV === 'production' ? undefined : false,
- crossOriginEmbedderPolicy: false, // Allow WebSocket connections
- }));
-
- // CORS middleware
- app.use(corsMiddleware);
-
- // Compression middleware
- app.use(compression());
-
- // Body parsing middleware
- app.use(express.json({
- limit: process.env.REQUEST_SIZE_LIMIT || '10mb',
- verify: (req, res, buf) => {
- // Store raw body for webhook verification if needed
- req.rawBody = buf;
- },
- }));
- app.use(express.urlencoded({
- extended: true,
- limit: process.env.REQUEST_SIZE_LIMIT || '10mb',
- }));
-
- // Cookie parsing middleware
- app.use(cookieParser());
-
- // Request logging middleware
- app.use(requestLogger);
-
- // Rate limiting middleware
- app.use(rateLimiters.global);
-
- // Health check endpoint (before other routes)
- app.get('/health', (req, res) => {
- const healthData = {
- status: 'healthy',
- timestamp: new Date().toISOString(),
- version: process.env.npm_package_version || '0.1.0',
- environment: NODE_ENV,
- uptime: process.uptime(),
- memory: {
- used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
- total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
- rss: Math.round(process.memoryUsage().rss / 1024 / 1024),
- },
- };
-
- res.status(200).json(healthData);
- });
-
- // API routes
- app.use('/', routes);
-
- // 404 handler for unmatched routes
- app.use('*', (req, res) => {
- logger.warn('Route not found', {
- correlationId: req.correlationId,
- method: req.method,
- url: req.originalUrl,
- ip: req.ip,
- userAgent: req.get('User-Agent'),
+ // Add correlation ID to all requests for tracing
+ app.use((req, res, next) => {
+ req.correlationId = uuidv4();
+ res.set('X-Correlation-ID', req.correlationId);
+ next();
});
- res.status(404).json({
- error: 'Not Found',
- message: 'The requested resource was not found',
- path: req.originalUrl,
- timestamp: new Date().toISOString(),
- correlationId: req.correlationId,
+ // Security middleware
+ app.use(helmet({
+ contentSecurityPolicy: NODE_ENV === 'production' ? undefined : false,
+ crossOriginEmbedderPolicy: false, // Allow WebSocket connections
+ }));
+
+ // CORS middleware
+ app.use(corsMiddleware);
+
+ // Compression middleware
+ app.use(compression());
+
+ // Body parsing middleware
+ app.use(express.json({
+ limit: process.env.REQUEST_SIZE_LIMIT || '10mb',
+ verify: (req, res, buf) => {
+ // Store raw body for webhook verification if needed
+ req.rawBody = buf;
+ }
+ }));
+ app.use(express.urlencoded({
+ extended: true,
+ limit: process.env.REQUEST_SIZE_LIMIT || '10mb'
+ }));
+
+ // Cookie parsing middleware
+ app.use(cookieParser());
+
+ // Request logging middleware
+ app.use(requestLogger);
+
+ // Rate limiting middleware
+ app.use(rateLimiters.global);
+
+ // Health check endpoint (before other routes)
+ app.get('/health', (req, res) => {
+ const healthData = {
+ status: 'healthy',
+ timestamp: new Date().toISOString(),
+ version: process.env.npm_package_version || '0.1.0',
+ environment: NODE_ENV,
+ uptime: process.uptime(),
+ memory: {
+ used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
+ total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
+ rss: Math.round(process.memoryUsage().rss / 1024 / 1024)
+ }
+ };
+
+ res.status(200).json(healthData);
});
- });
- // Global error handler (must be last)
- app.use(errorHandler);
+ // API routes
+ app.use('/', routes);
- return app;
+ // 404 handler for unmatched routes
+ app.use('*', (req, res) => {
+ logger.warn('Route not found', {
+ correlationId: req.correlationId,
+ method: req.method,
+ url: req.originalUrl,
+ ip: req.ip,
+ userAgent: req.get('User-Agent')
+ });
+
+ res.status(404).json({
+ error: 'Not Found',
+ message: 'The requested resource was not found',
+ path: req.originalUrl,
+ timestamp: new Date().toISOString(),
+ correlationId: req.correlationId
+ });
+ });
+
+ // Global error handler (must be last)
+ app.use(errorHandler);
+
+ return app;
}
-module.exports = createApp;
+module.exports = createApp;
\ No newline at end of file
diff --git a/src/config/email.js b/src/config/email.js
deleted file mode 100644
index ede1840..0000000
--- a/src/config/email.js
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * Email Configuration
- * Centralized email service configuration with environment-based setup
- */
-
-const logger = require('../utils/logger');
-
-/**
- * Email service configuration based on environment
- */
-const emailConfig = {
- // Development configuration (console logging)
- development: {
- provider: 'mock',
- settings: {
- host: 'localhost',
- port: 1025,
- secure: false,
- logger: true,
- },
- },
-
- // Production configuration (actual SMTP)
- production: {
- provider: 'smtp',
- settings: {
- host: process.env.SMTP_HOST,
- port: parseInt(process.env.SMTP_PORT) || 587,
- secure: process.env.SMTP_SECURE === 'true',
- auth: {
- user: process.env.SMTP_USER,
- pass: process.env.SMTP_PASS,
- },
- },
- },
-
- // Test configuration (nodemailer test accounts)
- test: {
- provider: 'test',
- settings: {
- host: 'smtp.ethereal.email',
- port: 587,
- secure: false,
- auth: {
- user: 'ethereal.user@ethereal.email',
- pass: 'ethereal.pass',
- },
- },
- },
-};
-
-/**
- * Get current email configuration based on environment
- * @returns {Object} Email configuration
- */
-function getEmailConfig() {
- const env = process.env.NODE_ENV || 'development';
- const config = emailConfig[env] || emailConfig.development;
-
- logger.info('Email configuration loaded', {
- environment: env,
- provider: config.provider,
- host: config.settings.host,
- port: config.settings.port,
- });
-
- return config;
-}
-
-/**
- * Validate email configuration
- * @param {Object} config - Email configuration to validate
- * @returns {Object} Validation result
- */
-function validateEmailConfig(config) {
- const errors = [];
-
- if (!config) {
- errors.push('Email configuration is missing');
- return { isValid: false, errors };
- }
-
- if (!config.settings) {
- errors.push('Email settings are missing');
- return { isValid: false, errors };
- }
-
- // Skip validation for mock/development mode
- if (config.provider === 'mock') {
- return { isValid: true, errors: [] };
- }
-
- const { settings } = config;
-
- if (!settings.host) {
- errors.push('SMTP host is required');
- }
-
- if (!settings.port) {
- errors.push('SMTP port is required');
- }
-
- if (config.provider === 'smtp' && (!settings.auth || !settings.auth.user || !settings.auth.pass)) {
- errors.push('SMTP authentication credentials are required for production');
- }
-
- return {
- isValid: errors.length === 0,
- errors,
- };
-}
-
-/**
- * Email templates configuration
- */
-const emailTemplates = {
- verification: {
- subject: 'Verify Your Shattered Void Account',
- template: 'email-verification',
- },
- passwordReset: {
- subject: 'Reset Your Shattered Void Password',
- template: 'password-reset',
- },
- securityAlert: {
- subject: 'Security Alert - Shattered Void',
- template: 'security-alert',
- },
- welcomeComplete: {
- subject: 'Welcome to Shattered Void!',
- template: 'welcome-complete',
- },
- passwordChanged: {
- subject: 'Password Changed - Shattered Void',
- template: 'password-changed',
- },
-};
-
-/**
- * Email sending configuration
- */
-const sendingConfig = {
- from: {
- name: process.env.SMTP_FROM_NAME || 'Shattered Void',
- address: process.env.SMTP_FROM || 'noreply@shatteredvoid.game',
- },
- replyTo: {
- name: process.env.SMTP_REPLY_NAME || 'Shattered Void Support',
- address: process.env.SMTP_REPLY_TO || 'support@shatteredvoid.game',
- },
- defaults: {
- headers: {
- 'X-Mailer': 'Shattered Void Game Server v1.0',
- 'X-Priority': '3',
- },
- },
- rateLimiting: {
- maxPerHour: parseInt(process.env.EMAIL_RATE_LIMIT) || 100,
- maxPerDay: parseInt(process.env.EMAIL_DAILY_LIMIT) || 1000,
- },
-};
-
-/**
- * Development email configuration with additional debugging
- */
-const developmentConfig = {
- logEmails: true,
- saveEmailsToFile: process.env.SAVE_DEV_EMAILS === 'true',
- emailLogPath: process.env.EMAIL_LOG_PATH || './logs/emails.log',
- mockDelay: parseInt(process.env.MOCK_EMAIL_DELAY) || 0, // Simulate network delay
-};
-
-/**
- * Environment-specific email service factory
- * @returns {Object} Email service configuration with methods
- */
-function createEmailServiceConfig() {
- const config = getEmailConfig();
- const validation = validateEmailConfig(config);
-
- if (!validation.isValid) {
- logger.error('Invalid email configuration', {
- errors: validation.errors,
- });
-
- if (process.env.NODE_ENV === 'production') {
- throw new Error(`Email configuration validation failed: ${validation.errors.join(', ')}`);
- }
- }
-
- return {
- ...config,
- templates: emailTemplates,
- sending: sendingConfig,
- development: developmentConfig,
- validation,
-
- /**
- * Get template configuration
- * @param {string} templateName - Template name
- * @returns {Object} Template configuration
- */
- getTemplate(templateName) {
- const template = emailTemplates[templateName];
- if (!template) {
- throw new Error(`Email template '${templateName}' not found`);
- }
- return template;
- },
-
- /**
- * Get sender information
- * @returns {Object} Sender configuration
- */
- getSender() {
- return {
- from: `${sendingConfig.from.name} <${sendingConfig.from.address}>`,
- replyTo: `${sendingConfig.replyTo.name} <${sendingConfig.replyTo.address}>`,
- };
- },
-
- /**
- * Check if rate limiting allows sending
- * @param {string} identifier - Rate limiting identifier (email/IP)
- * @returns {Promise} Whether sending is allowed
- */
- async checkRateLimit(identifier) {
- // TODO: Implement rate limiting check with Redis
- // For now, always allow
- return true;
- },
- };
-}
-
-module.exports = {
- getEmailConfig,
- validateEmailConfig,
- createEmailServiceConfig,
- emailTemplates,
- sendingConfig,
- developmentConfig,
-};
\ No newline at end of file
diff --git a/src/config/redis.js b/src/config/redis.js
index 5ea4aec..bbfeb9e 100644
--- a/src/config/redis.js
+++ b/src/config/redis.js
@@ -8,15 +8,15 @@ const logger = require('../utils/logger');
// Configuration
const REDIS_CONFIG = {
- host: process.env.REDIS_HOST || 'localhost',
- port: parseInt(process.env.REDIS_PORT) || 6379,
- password: process.env.REDIS_PASSWORD || undefined,
- db: parseInt(process.env.REDIS_DB) || 0,
- retryDelayOnFailover: 100,
- maxRetriesPerRequest: 3,
- lazyConnect: true,
- connectTimeout: 10000,
- commandTimeout: 5000,
+ host: process.env.REDIS_HOST || 'localhost',
+ port: parseInt(process.env.REDIS_PORT) || 6379,
+ password: process.env.REDIS_PASSWORD || undefined,
+ db: parseInt(process.env.REDIS_DB) || 0,
+ retryDelayOnFailover: 100,
+ maxRetriesPerRequest: 3,
+ lazyConnect: true,
+ connectTimeout: 10000,
+ commandTimeout: 5000,
};
let client = null;
@@ -27,59 +27,59 @@ let isConnected = false;
* @returns {Object} Redis client instance
*/
function createRedisClient() {
- const redisClient = redis.createClient({
- socket: {
- host: REDIS_CONFIG.host,
- port: REDIS_CONFIG.port,
- connectTimeout: REDIS_CONFIG.connectTimeout,
- commandTimeout: REDIS_CONFIG.commandTimeout,
- reconnectStrategy: (retries) => {
- if (retries > 10) {
- logger.error('Redis reconnection failed after 10 attempts');
- return new Error('Redis reconnection failed');
- }
- const delay = Math.min(retries * 50, 2000);
- logger.warn(`Redis reconnecting in ${delay}ms (attempt ${retries})`);
- return delay;
- },
- },
- password: REDIS_CONFIG.password,
- database: REDIS_CONFIG.db,
- });
-
- // Connection event handlers
- redisClient.on('connect', () => {
- logger.info('Redis client connected');
- });
-
- redisClient.on('ready', () => {
- isConnected = true;
- logger.info('Redis client ready', {
- host: REDIS_CONFIG.host,
- port: REDIS_CONFIG.port,
- database: REDIS_CONFIG.db,
+ const redisClient = redis.createClient({
+ socket: {
+ host: REDIS_CONFIG.host,
+ port: REDIS_CONFIG.port,
+ connectTimeout: REDIS_CONFIG.connectTimeout,
+ commandTimeout: REDIS_CONFIG.commandTimeout,
+ reconnectStrategy: (retries) => {
+ if (retries > 10) {
+ logger.error('Redis reconnection failed after 10 attempts');
+ return new Error('Redis reconnection failed');
+ }
+ const delay = Math.min(retries * 50, 2000);
+ logger.warn(`Redis reconnecting in ${delay}ms (attempt ${retries})`);
+ return delay;
+ }
+ },
+ password: REDIS_CONFIG.password,
+ database: REDIS_CONFIG.db,
});
- });
- redisClient.on('error', (error) => {
- isConnected = false;
- logger.error('Redis client error:', {
- message: error.message,
- code: error.code,
- stack: error.stack,
+ // Connection event handlers
+ redisClient.on('connect', () => {
+ logger.info('Redis client connected');
});
- });
- redisClient.on('end', () => {
- isConnected = false;
- logger.info('Redis client connection ended');
- });
+ redisClient.on('ready', () => {
+ isConnected = true;
+ logger.info('Redis client ready', {
+ host: REDIS_CONFIG.host,
+ port: REDIS_CONFIG.port,
+ database: REDIS_CONFIG.db
+ });
+ });
- redisClient.on('reconnecting', () => {
- logger.info('Redis client reconnecting...');
- });
+ redisClient.on('error', (error) => {
+ isConnected = false;
+ logger.error('Redis client error:', {
+ message: error.message,
+ code: error.code,
+ stack: error.stack
+ });
+ });
- return redisClient;
+ redisClient.on('end', () => {
+ isConnected = false;
+ logger.info('Redis client connection ended');
+ });
+
+ redisClient.on('reconnecting', () => {
+ logger.info('Redis client reconnecting...');
+ });
+
+ return redisClient;
}
/**
@@ -87,33 +87,33 @@ function createRedisClient() {
* @returns {Promise