Troubleshooting - RumenDamyanov/js-chess GitHub Wiki

Troubleshooting

Comprehensive troubleshooting guide for common issues and problems in the chess showcase application.

Overview

This guide covers troubleshooting for:

  • Development environment setup issues
  • Build and compilation problems
  • Runtime errors and bugs
  • Performance issues
  • Network and connectivity problems
  • Chess engine and game logic issues
  • Framework-specific problems

Development Environment Issues

Node.js and npm Problems

Issue: npm install fails with permission errors

# Error message
npm ERR! code EACCES
npm ERR! syscall rename
npm ERR! path /usr/local/lib/node_modules/npm
npm ERR! errno -13

Solution:

# Fix npm permissions (macOS/Linux)
sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/local/lib/node_modules

# Or use nvm for better Node.js management
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 18
nvm use 18

Issue: Module not found errors

Error: Cannot resolve module 'some-package'

Solution:

# Clear npm cache
npm cache clean --force

# Delete node_modules and reinstall
rm -rf node_modules package-lock.json
npm install

# Check for conflicting package versions
npm ls
npm audit fix

Issue: Version compatibility problems

npm WARN peer dep missing: react@^17.0.0

Solution:

# Check package compatibility
npm ls react
npm info react versions --json

# Install compatible versions
npm install react@^17.0.0 react-dom@^17.0.0

# Use npm-check-updates for major updates
npx npm-check-updates -u
npm install

Git and Version Control Issues

Issue: Merge conflicts in package-lock.json

<<<<<<< HEAD
  "lockfileVersion": 2,
=======
  "lockfileVersion": 1,
>>>>>>> feature-branch

Solution:

# Delete conflicted lock file and regenerate
rm package-lock.json
npm install

# Add to git
git add package-lock.json
git commit -m "Regenerate package-lock.json"

Issue: Large files in Git history

remote: error: File dist/chess-engine.wasm is 134.00 MB; this exceeds GitHub's file size limit of 100.00 MB

Solution:

# Remove large files from history
git filter-branch --tree-filter 'rm -rf dist/' HEAD

# Use Git LFS for large files
git lfs track "*.wasm"
git add .gitattributes
git add file.wasm
git commit -m "Track large files with Git LFS"

Build and Compilation Issues

TypeScript Compilation Errors

Issue: Type definition conflicts

// Error: Duplicate identifier 'ChessPosition'
interface ChessPosition {
  rank: number;
  file: number;
}

Solution:

// Use module augmentation or namespaces
declare namespace Chess {
  interface Position {
    rank: number;
    file: number;
  }
}

// Or use different names
interface ChessBoardPosition {
  rank: number;
  file: number;
}

Issue: Import path resolution problems

// Error: Cannot find module '../../shared/chess'
import { ChessEngine } from '../../shared/chess';

Solution:

// tsconfig.json - Add path mapping
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@shared/*": ["shared/*"],
      "@chess/*": ["shared/chess/*"]
    }
  }
}
// Use the mapped paths
import { ChessEngine } from '@chess/engine';

Webpack Build Issues

Issue: Bundle size too large

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB).

Solution:

// webpack.config.js - Add code splitting
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        chess: {
          test: /[\\/]src[\\/]chess[\\/]/,
          name: 'chess-engine',
          chunks: 'all',
        }
      }
    }
  }
};

Issue: Module resolution failures

Module not found: Error: Can't resolve 'fs' in '/path/to/project'

Solution:

// webpack.config.js - Add fallbacks for Node.js modules
module.exports = {
  resolve: {
    fallback: {
      "fs": false,
      "path": require.resolve("path-browserify"),
      "crypto": require.resolve("crypto-browserify")
    }
  }
};

Angular-Specific Issues

Issue: Zone.js patch conflicts

Error: Zone already loaded

Solution:

// main.ts - Import zone.js only once
import 'zone.js/dist/zone';
// Remove other zone.js imports

// Or configure zone.js
(window as any).__Zone_disable_requestAnimationFrame = true;
(window as any).__Zone_disable_on_property = true;

Issue: Standalone component errors

// Error: Component is not standalone
@Component({
  selector: 'app-chess-board',
  template: '<div>Chess Board</div>'
})
export class ChessBoardComponent { }

Solution:

// Make component standalone
@Component({
  selector: 'app-chess-board',
  standalone: true,
  imports: [CommonModule],
  template: '<div>Chess Board</div>'
})
export class ChessBoardComponent { }

React-Specific Issues

Issue: Hook dependency warnings

// Warning: React Hook useEffect has a missing dependency
useEffect(() => {
  fetchGameData(gameId);
}, []); // Missing gameId dependency

Solution:

// Add missing dependencies
useEffect(() => {
  fetchGameData(gameId);
}, [gameId]);

// Or use useCallback for stable references
const fetchData = useCallback(() => {
  fetchGameData(gameId);
}, [gameId]);

useEffect(() => {
  fetchData();
}, [fetchData]);

Issue: State update on unmounted component

// Warning: Can't perform a React state update on an unmounted component

Solution:

// Use cleanup function and ref
function ChessGame() {
  const [gameState, setGameState] = useState(null);
  const mounted = useRef(true);

  useEffect(() => {
    return () => {
      mounted.current = false;
    };
  }, []);

  const updateGameState = (newState) => {
    if (mounted.current) {
      setGameState(newState);
    }
  };

  // Or use AbortController for fetch requests
  useEffect(() => {
    const controller = new AbortController();

    fetch('/api/game', { signal: controller.signal })
      .then(response => response.json())
      .then(data => {
        if (!controller.signal.aborted) {
          setGameState(data);
        }
      });

    return () => controller.abort();
  }, []);
}

Vue-Specific Issues

Issue: Composition API reactivity issues

// Reactivity not working
const gameState = {
  board: [],
  currentPlayer: 'white'
};

Solution:

// Use reactive or ref
import { reactive, ref } from 'vue';

const gameState = reactive({
  board: [],
  currentPlayer: 'white'
});

// Or with ref
const gameState = ref({
  board: [],
  currentPlayer: 'white'
});

Issue: Template compilation errors

<!-- Error: Property 'gameState' does not exist -->
<template>
  <div>{{ gameState.currentPlayer }}</div>
</template>

Solution:

<script setup>
import { ref } from 'vue';

// Define reactive state
const gameState = ref({
  currentPlayer: 'white'
});
</script>

<template>
  <div>{{ gameState.currentPlayer }}</div>
</template>

Runtime Errors

Chess Engine Errors

Issue: Invalid move validation

// Error: Move validation failed for valid move
const move = { from: 'e2', to: 'e4' };
const isValid = chessEngine.isValidMove(move); // Returns false incorrectly

Diagnosis:

// Debug the game state
console.log('Current board state:', chessEngine.getBoard());
console.log('Current player:', chessEngine.getCurrentPlayer());
console.log('Move being validated:', move);

// Check piece at source square
const piece = chessEngine.getPieceAt('e2');
console.log('Piece at e2:', piece);

// Check if move follows chess rules
const legalMoves = chessEngine.getLegalMoves('e2');
console.log('Legal moves from e2:', legalMoves);

Solution:

// Ensure correct game state initialization
const chessEngine = new ChessEngine();
chessEngine.initializeBoard(); // Make sure board is set up correctly

// Validate move format
function validateMoveFormat(move) {
  if (!move || typeof move !== 'object') {
    throw new Error('Move must be an object');
  }

  if (!move.from || !move.to) {
    throw new Error('Move must have from and to properties');
  }

  if (!/^[a-h][1-8]$/.test(move.from) || !/^[a-h][1-8]$/.test(move.to)) {
    throw new Error('Invalid square notation');
  }

  return true;
}

// Use validated move
try {
  validateMoveFormat(move);
  const isValid = chessEngine.isValidMove(move);
  console.log('Move is valid:', isValid);
} catch (error) {
  console.error('Move validation error:', error.message);
}

Issue: Chess engine crashes or hangs

// Engine stops responding after complex position

Diagnosis:

// Add timeout to engine calls
function callEngineWithTimeout(fn, timeout = 5000) {
  return Promise.race([
    fn(),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Engine timeout')), timeout)
    )
  ]);
}

// Monitor engine performance
const startTime = performance.now();
try {
  const result = await callEngineWithTimeout(() =>
    chessEngine.getBestMove(position)
  );
  const duration = performance.now() - startTime;
  console.log('Engine call completed in', duration, 'ms');
} catch (error) {
  console.error('Engine error:', error);
}

Solution:

// Implement engine recovery
class RobustChessEngine {
  constructor() {
    this.engine = new ChessEngine();
    this.backupEngine = new ChessEngine();
    this.maxRetries = 3;
  }

  async callEngine(method, ...args) {
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        const result = await this.engine[method](...args);
        return result;
      } catch (error) {
        console.warn(`Engine attempt ${attempt} failed:`, error);

        if (attempt === this.maxRetries) {
          // Use backup engine
          try {
            return await this.backupEngine[method](...args);
          } catch (backupError) {
            throw new Error(`Both engines failed: ${error.message}, ${backupError.message}`);
          }
        }

        // Reset engine for retry
        this.engine = new ChessEngine();
        this.engine.loadPosition(this.getCurrentPosition());
      }
    }
  }

  getCurrentPosition() {
    // Return current game state for engine recovery
    return this.engine.getFEN();
  }
}

Network and API Errors

Issue: CORS errors in development

Access to fetch at 'http://localhost:8080/api/games' from origin 'http://localhost:3000' has been blocked by CORS policy

Solution:

// Backend - Express.js CORS setup
const cors = require('cors');

app.use(cors({
  origin: ['http://localhost:3000', 'http://localhost:3001'],
  credentials: true
}));

// Or development proxy in package.json
{
  "name": "chess-frontend",
  "proxy": "http://localhost:8080"
}

// Vite proxy configuration
// vite.config.js
export default {
  server: {
    proxy: {
      '/api': 'http://localhost:8080'
    }
  }
}

Issue: WebSocket connection failures

// WebSocket connection failed
WebSocket connection to 'ws://localhost:8080/ws' failed: Error in connection establishment

Diagnosis:

// Test WebSocket connectivity
function testWebSocketConnection(url) {
  return new Promise((resolve, reject) => {
    const ws = new WebSocket(url);

    ws.onopen = () => {
      console.log('WebSocket connection successful');
      ws.close();
      resolve(true);
    };

    ws.onerror = (error) => {
      console.error('WebSocket connection failed:', error);
      reject(error);
    };

    ws.onclose = (event) => {
      console.log('WebSocket closed:', event.code, event.reason);
    };

    // Timeout after 5 seconds
    setTimeout(() => {
      if (ws.readyState === WebSocket.CONNECTING) {
        ws.close();
        reject(new Error('Connection timeout'));
      }
    }, 5000);
  });
}

// Test different protocols
const wsUrls = [
  'ws://localhost:8080/ws',
  'wss://localhost:8080/ws',
  'ws://127.0.0.1:8080/ws'
];

for (const url of wsUrls) {
  try {
    await testWebSocketConnection(url);
    console.log('Working URL:', url);
    break;
  } catch (error) {
    console.log('Failed URL:', url, error.message);
  }
}

Solution:

// Robust WebSocket implementation
class RobustWebSocket {
  constructor(url, options = {}) {
    this.url = url;
    this.options = {
      reconnectInterval: 1000,
      maxReconnectAttempts: 5,
      ...options
    };
    this.reconnectAttempts = 0;
    this.connect();
  }

  connect() {
    try {
      this.ws = new WebSocket(this.url);
      this.setupEventHandlers();
    } catch (error) {
      console.error('WebSocket creation failed:', error);
      this.handleReconnect();
    }
  }

  setupEventHandlers() {
    this.ws.onopen = () => {
      console.log('WebSocket connected');
      this.reconnectAttempts = 0;
      this.onConnect?.();
    };

    this.ws.onmessage = (event) => {
      this.onMessage?.(event);
    };

    this.ws.onclose = (event) => {
      console.log('WebSocket closed:', event.code, event.reason);

      // Attempt reconnection for unexpected closures
      if (event.code !== 1000) {
        this.handleReconnect();
      }
    };

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
      this.onError?.(error);
    };
  }

  handleReconnect() {
    if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
      console.error('Max reconnection attempts reached');
      return;
    }

    this.reconnectAttempts++;
    const delay = this.options.reconnectInterval * this.reconnectAttempts;

    console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);

    setTimeout(() => {
      this.connect();
    }, delay);
  }

  send(data) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    } else {
      console.warn('WebSocket not connected, message queued');
      // Implement message queuing if needed
    }
  }

  close() {
    if (this.ws) {
      this.ws.close(1000, 'Client closing');
    }
  }
}

Memory and Performance Issues

Issue: Memory leaks in long-running games

// Memory usage keeps increasing during gameplay

Diagnosis:

// Monitor memory usage
function monitorMemory() {
  if (performance.memory) {
    console.log('Memory usage:', {
      used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024) + ' MB',
      total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024) + ' MB',
      limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024) + ' MB'
    });
  }
}

// Check every 10 seconds
setInterval(monitorMemory, 10000);

// Profile specific functions
function profileFunction(fn, name) {
  return function(...args) {
    const start = performance.now();
    const memBefore = performance.memory?.usedJSHeapSize || 0;

    const result = fn.apply(this, args);

    const duration = performance.now() - start;
    const memAfter = performance.memory?.usedJSHeapSize || 0;
    const memDelta = memAfter - memBefore;

    console.log(`${name}:`, {
      duration: `${duration.toFixed(2)}ms`,
      memoryDelta: `${(memDelta / 1024).toFixed(2)}KB`
    });

    return result;
  };
}

Solution:

// Clean up event listeners
class ChessGame {
  constructor() {
    this.boundHandlers = {
      handleKeyPress: this.handleKeyPress.bind(this),
      handleMouseMove: this.handleMouseMove.bind(this)
    };
  }

  addEventListeners() {
    document.addEventListener('keypress', this.boundHandlers.handleKeyPress);
    document.addEventListener('mousemove', this.boundHandlers.handleMouseMove);
  }

  removeEventListeners() {
    document.removeEventListener('keypress', this.boundHandlers.handleKeyPress);
    document.removeEventListener('mousemove', this.boundHandlers.handleMouseMove);
  }

  destroy() {
    this.removeEventListeners();
    // Clear any intervals or timeouts
    if (this.gameTimer) {
      clearInterval(this.gameTimer);
    }
    // Clear references
    this.gameState = null;
    this.players = null;
  }
}

// Use WeakMap for object associations
const gameStates = new WeakMap();
const playerData = new WeakMap();

// Instead of storing references in objects
function associateGameState(game, state) {
  gameStates.set(game, state);
}

function getGameState(game) {
  return gameStates.get(game);
}

Database and Storage Issues

Issue: Database connection failures

Error: connect ECONNREFUSED 127.0.0.1:5432

Diagnosis:

// Test database connectivity
async function testDatabaseConnection() {
  try {
    const db = new Database(process.env.DATABASE_URL);
    await db.connect();

    // Test basic query
    const result = await db.query('SELECT 1 as test');
    console.log('Database test successful:', result);

    await db.close();
    return true;
  } catch (error) {
    console.error('Database connection failed:', error);
    return false;
  }
}

// Check database status
async function checkDatabaseStatus() {
  const checks = {
    connection: false,
    tables: false,
    permissions: false
  };

  try {
    const db = new Database(process.env.DATABASE_URL);
    await db.connect();
    checks.connection = true;

    // Check required tables exist
    const tables = await db.query(`
      SELECT table_name
      FROM information_schema.tables
      WHERE table_schema = 'public'
    `);

    const requiredTables = ['games', 'users', 'moves'];
    const existingTables = tables.map(t => t.table_name);
    checks.tables = requiredTables.every(table => existingTables.includes(table));

    // Check permissions
    await db.query('INSERT INTO games (id) VALUES ($1) ON CONFLICT DO NOTHING', ['test']);
    await db.query('DELETE FROM games WHERE id = $1', ['test']);
    checks.permissions = true;

    await db.close();
  } catch (error) {
    console.error('Database check failed:', error);
  }

  return checks;
}

Solution:

// Database connection with retry logic
class DatabaseConnection {
  constructor(connectionString) {
    this.connectionString = connectionString;
    this.pool = null;
    this.maxRetries = 5;
    this.retryDelay = 1000;
  }

  async connect() {
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        this.pool = new Pool({
          connectionString: this.connectionString,
          max: 20,
          idleTimeoutMillis: 30000,
          connectionTimeoutMillis: 2000
        });

        // Test connection
        const client = await this.pool.connect();
        await client.query('SELECT NOW()');
        client.release();

        console.log('Database connected successfully');
        return;
      } catch (error) {
        console.error(`Database connection attempt ${attempt} failed:`, error.message);

        if (attempt === this.maxRetries) {
          throw new Error(`Failed to connect after ${this.maxRetries} attempts`);
        }

        await this.delay(this.retryDelay * attempt);
      }
    }
  }

  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  async query(text, params) {
    if (!this.pool) {
      throw new Error('Database not connected');
    }

    try {
      const result = await this.pool.query(text, params);
      return result;
    } catch (error) {
      console.error('Database query error:', error);

      // Check if connection was lost
      if (error.code === 'ECONNRESET' || error.code === 'ENOTFOUND') {
        console.log('Connection lost, attempting to reconnect...');
        await this.connect();
        return await this.pool.query(text, params);
      }

      throw error;
    }
  }
}

Issue: LocalStorage quota exceeded

// QuotaExceededError: Failed to execute 'setItem' on 'Storage'

Solution:

// Safe localStorage with quota management
class SafeStorage {
  constructor(storageType = 'localStorage') {
    this.storage = window[storageType];
    this.maxSize = 5 * 1024 * 1024; // 5MB typical limit
  }

  setItem(key, value) {
    try {
      const serialized = JSON.stringify(value);

      // Check if item would exceed quota
      const currentSize = this.getStorageSize();
      const itemSize = serialized.length * 2; // Rough estimate (UTF-16)

      if (currentSize + itemSize > this.maxSize) {
        this.cleanup();
      }

      this.storage.setItem(key, serialized);
    } catch (error) {
      if (error.name === 'QuotaExceededError') {
        console.warn('Storage quota exceeded, cleaning up...');
        this.cleanup();

        // Try again after cleanup
        try {
          this.storage.setItem(key, JSON.stringify(value));
        } catch (retryError) {
          console.error('Storage still full after cleanup');
          throw retryError;
        }
      } else {
        throw error;
      }
    }
  }

  getItem(key) {
    try {
      const item = this.storage.getItem(key);
      return item ? JSON.parse(item) : null;
    } catch (error) {
      console.error(`Failed to parse storage item ${key}:`, error);
      this.storage.removeItem(key); // Remove corrupted item
      return null;
    }
  }

  cleanup() {
    const items = [];

    // Collect all items with timestamps
    for (let i = 0; i < this.storage.length; i++) {
      const key = this.storage.key(i);
      const value = this.storage.getItem(key);

      try {
        const parsed = JSON.parse(value);
        items.push({
          key,
          value,
          timestamp: parsed.timestamp || 0,
          size: value.length * 2
        });
      } catch (error) {
        // Remove corrupted items
        this.storage.removeItem(key);
      }
    }

    // Sort by timestamp (oldest first)
    items.sort((a, b) => a.timestamp - b.timestamp);

    // Remove oldest items until under 80% capacity
    const targetSize = this.maxSize * 0.8;
    let currentSize = this.getStorageSize();

    for (const item of items) {
      if (currentSize <= targetSize) break;

      this.storage.removeItem(item.key);
      currentSize -= item.size;
      console.log(`Removed storage item: ${item.key}`);
    }
  }

  getStorageSize() {
    let total = 0;
    for (let key in this.storage) {
      if (this.storage.hasOwnProperty(key)) {
        total += this.storage[key].length * 2;
      }
    }
    return total;
  }

  // Add timestamp to stored data
  setItemWithTimestamp(key, value) {
    const dataWithTimestamp = {
      ...value,
      timestamp: Date.now()
    };
    this.setItem(key, dataWithTimestamp);
  }
}

const safeStorage = new SafeStorage();

Debugging Techniques

Browser DevTools Debugging

// Debug chess game state
function debugGameState() {
  console.group('Chess Game Debug Info');

  console.log('Board State:', window.chessGame?.getBoard());
  console.log('Current Player:', window.chessGame?.getCurrentPlayer());
  console.log('Game History:', window.chessGame?.getHistory());
  console.log('Available Moves:', window.chessGame?.getLegalMoves());

  // Check for JavaScript errors
  console.log('Recent Errors:', window.errors || []);

  // Performance metrics
  if (performance.memory) {
    console.log('Memory Usage:', {
      used: `${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB`,
      total: `${Math.round(performance.memory.totalJSHeapSize / 1024 / 1024)}MB`
    });
  }

  console.groupEnd();
}

// Make debug function globally available
window.debugChess = debugGameState;

// Debug WebSocket connections
function debugWebSocket() {
  console.group('WebSocket Debug Info');

  const ws = window.chessWebSocket;
  if (ws) {
    console.log('WebSocket State:', {
      readyState: ws.readyState,
      url: ws.url,
      protocol: ws.protocol
    });

    console.log('Connection States:', {
      CONNECTING: WebSocket.CONNECTING,
      OPEN: WebSocket.OPEN,
      CLOSING: WebSocket.CLOSING,
      CLOSED: WebSocket.CLOSED
    });
  } else {
    console.log('No WebSocket connection found');
  }

  console.groupEnd();
}

window.debugWebSocket = debugWebSocket;

Logging for Production Debugging

// Production-safe debug logging
class DebugLogger {
  constructor() {
    this.enabled = process.env.NODE_ENV === 'development' ||
                   localStorage.getItem('debug-mode') === 'true';
    this.logs = [];
    this.maxLogs = 100;
  }

  log(level, message, data = {}) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      data,
      url: window.location.href,
      userAgent: navigator.userAgent
    };

    this.logs.push(logEntry);

    // Keep only recent logs
    if (this.logs.length > this.maxLogs) {
      this.logs = this.logs.slice(-this.maxLogs);
    }

    if (this.enabled) {
      console[level](message, data);
    }

    // Send critical errors to monitoring
    if (level === 'error') {
      this.sendToMonitoring(logEntry);
    }
  }

  error(message, data) {
    this.log('error', message, data);
  }

  warn(message, data) {
    this.log('warn', message, data);
  }

  info(message, data) {
    this.log('info', message, data);
  }

  debug(message, data) {
    this.log('debug', message, data);
  }

  exportLogs() {
    const logData = {
      timestamp: new Date().toISOString(),
      logs: this.logs,
      userAgent: navigator.userAgent,
      url: window.location.href
    };

    const blob = new Blob([JSON.stringify(logData, null, 2)], {
      type: 'application/json'
    });

    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `chess-debug-${Date.now()}.json`;
    a.click();

    URL.revokeObjectURL(url);
  }

  async sendToMonitoring(logEntry) {
    try {
      await fetch('/api/debug/log', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(logEntry)
      });
    } catch (error) {
      // Fail silently in production
    }
  }

  enableDebugMode() {
    localStorage.setItem('debug-mode', 'true');
    this.enabled = true;
    console.log('Debug mode enabled');
  }

  disableDebugMode() {
    localStorage.removeItem('debug-mode');
    this.enabled = false;
    console.log('Debug mode disabled');
  }
}

const debugLogger = new DebugLogger();

// Make available globally for debugging
window.debugLogger = debugLogger;
window.exportDebugLogs = () => debugLogger.exportLogs();

Getting Help

Community Resources

  • GitHub Issues: Report bugs and feature requests
  • Stack Overflow: Tag questions with chess-js and framework tags
  • Discord/Slack: Join the community chat for real-time help
  • Documentation: Check the wiki for detailed guides

Creating Effective Bug Reports

# Bug Report Template

## Environment
- OS: [e.g., macOS 12.6]
- Browser: [e.g., Chrome 108.0.5359.124]
- Node.js Version: [e.g., 18.12.1]
- Framework: [e.g., React 18.2.0]

## Steps to Reproduce
1. Start a new game
2. Make move e2-e4
3. AI responds with e7-e5
4. Click on knight at b1
5. Error occurs

## Expected Behavior
Knight should highlight valid moves

## Actual Behavior
JavaScript error: "Cannot read property 'type' of null"

## Additional Context
- Error started after updating to version 2.1.0
- Only happens with AI games, not human vs human
- Console shows additional errors: [paste console output]

## Debug Information
[Paste output from `window.debugChess()` or attach exported logs]

Emergency Recovery Procedures

// Reset application state
function emergencyReset() {
  console.log('Performing emergency reset...');

  // Clear all storage
  localStorage.clear();
  sessionStorage.clear();

  // Clear service worker cache
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.getRegistrations().then(registrations => {
      registrations.forEach(registration => registration.unregister());
    });
  }

  // Clear application cache
  if ('caches' in window) {
    caches.keys().then(cacheNames => {
      cacheNames.forEach(cacheName => caches.delete(cacheName));
    });
  }

  // Reload page
  window.location.reload(true);
}

// Make available globally
window.emergencyReset = emergencyReset;

// Safe mode activation
function activateSafeMode() {
  localStorage.setItem('safe-mode', 'true');
  sessionStorage.setItem('debug-mode', 'true');

  // Disable non-essential features
  window.disableAnimations = true;
  window.disableWebSocket = true;
  window.disableAI = true;

  console.log('Safe mode activated - refresh page to apply');
}

window.activateSafeMode = activateSafeMode;

Next Steps

  • Monitoring - Set up monitoring to prevent issues
  • Performance - Optimize application performance
  • Security - Security troubleshooting and incident response
⚠️ **GitHub.com Fallback** ⚠️