Manual Completo de JavaScript

Este manual cubre desde los fundamentos de JavaScript hasta conceptos avanzados, con ejemplos prácticos aplicables en entornos laborales y en sistemas Linux.

1. Introducción a JavaScript

1.1 ¿Qué es JavaScript?

JavaScript es un lenguaje de programación interpretado, orientado a objetos y multiplataforma. A diferencia de lo que su nombre podría sugerir, no tiene relación directa con Java.

1.2 Entornos de ejecución

JavaScript puede ejecutarse en diferentes entornos:

Ejemplo en Linux: Ejecutar JavaScript en terminal

Instalar Node.js en Linux (Debian/Ubuntu):

sudo apt update && sudo apt install nodejs

Crear un archivo JavaScript:

// hola.js
const mensaje = "Hola desde Linux!";
console.log(mensaje);

Ejecutar el script:

node hola.js

2. Fundamentos de JavaScript

2.1 Variables y constantes

// Variables (pueden cambiar)
let nombre = "Carlos";
nombre = "Juan"; // Correcto

// Constantes (no pueden cambiar)
const PI = 3.1416;
// PI = 3.14; // Error!

Nota: Evita usar var en código moderno. Prefiere let y const por su mejor manejo de scope.

2.2 Tipos de datos

Tipo Descripción Ejemplo
String Cadena de texto "Hola mundo"
Number Números (enteros o decimales) 42, 3.14
Boolean Verdadero o falso true, false
Array Colección ordenada [1, 2, 3]
Object Colección de pares clave-valor {nombre: "Juan", edad: 30}
Null Valor nulo intencional null
Undefined Variable no definida undefined

2.3 Operadores

// Aritméticos
let suma = 5 + 3; // 8
let modulo = 10 % 3; // 1

// Comparación
let igual = 5 == '5'; // true (solo valor)
let estrictamenteIgual = 5 === '5'; // false (valor y tipo)

// Lógicos
let and = true && false; // false
let or = true || false; // true
let not = !true; // false

3. Estructuras de control

3.1 Condicionales

// if-else
let edad = 18;

if (edad >= 18) {
    console.log("Eres mayor de edad");
} else {
    console.log("Eres menor de edad");
}

// switch
let dia = "Lunes";

switch (dia) {
    case "Lunes":
        console.log("Inicio de semana");
        break;
    case "Viernes":
        console.log("¡Fin de semana cerca!");
        break;
    default:
        console.log("Día laboral");
}

3.2 Bucles

// for
for (let i = 0; i < 5; i++) {
    console.log("Iteración:", i);
}

// while
let contador = 0;
while (contador < 3) {
    console.log("Contador:", contador);
    contador++;
}

// for...of (para arrays)
const frutas = ["manzana", "banana", "naranja"];
for (const fruta of frutas) {
    console.log(fruta);
}

4. Funciones

4.1 Declaración de funciones

// Función tradicional
function saludar(nombre) {
    return `Hola, ${nombre}`;
}

// Función flecha (arrow function)
const sumar = (a, b) => a + b;

// Ejemplo práctico: procesar archivos en Linux
const procesarArchivos = (directorio, extension) => {
    // Simulación: en un entorno real usaríamos el módulo 'fs' de Node.js
    const archivos = [
        "documento.txt",
        "imagen.jpg",
        "reporte.pdf",
        "config.json"
    ];
    
    return archivos.filter(archivo => archivo.endsWith(extension));
};

// Uso en terminal Linux
const archivosJson = procesarArchivos("./", ".json");
console.log("Archivos JSON:", archivosJson);

4.2 Parámetros y argumentos

// Parámetros por defecto
function crearUsuario(nombre, rol = "usuario") {
    return { nombre, rol };
}

// Argumentos variables (rest parameters)
function sumarNumeros(...numeros) {
    return numeros.reduce((total, num) => total + num, 0);
}

// Ejemplo laboral: calcular total de ventas
const totalVentas = sumarNumeros(1200, 850, 430, 920);
console.log("Total de ventas:", totalVentas);

5. Trabajo con Arrays y Objetos

5.1 Métodos de arrays

const empleados = [
    { id: 1, nombre: "Ana", departamento: "Ventas", salario: 3200 },
    { id: 2, nombre: "Luis", departamento: "TI", salario: 3800 },
    { id: 3, nombre: "Marta", departamento: "Marketing", salario: 3500 },
    { id: 4, nombre: "Carlos", departamento: "Ventas", salario: 3100 }
];

// filter: filtrar empleados de ventas
const ventas = empleados.filter(emp => emp.departamento === "Ventas");

// map: crear array solo con nombres
const nombres = empleados.map(emp => emp.nombre);

// reduce: calcular nómina total
const nominaTotal = empleados.reduce((total, emp) => total + emp.salario, 0);

// sort: ordenar por salario (descendente)
const empleadosOrdenados = [...empleados].sort((a, b) => b.salario - a.salario);

// find: buscar empleado por ID
const empleado = empleados.find(emp => emp.id === 2);

5.2 Manipulación de objetos

// Creación de objetos
const servidor = {
    hostname: "web01",
    ip: "192.168.1.100",
    os: "Ubuntu 20.04",
    servicios: ["nginx", "mysql", "node"],
    verificarEstado() {
        // Simulación: en un entorno real podríamos ejecutar comandos SSH
        return `Servidor ${this.hostname} operativo`;
    }
};

// Acceder a propiedades
console.log(servidor.ip); // 192.168.1.100
console.log(servidor['os']); // Ubuntu 20.04

// Añadir nueva propiedad
servidor.ubicacion = "Centro de datos #2";

// Eliminar propiedad
delete servidor.servicios;

// Método del objeto
console.log(servidor.verificarEstado());

6. Programación Asíncrona

6.1 Callbacks

// Ejemplo: simular lectura de archivo en Linux
const leerArchivo = (ruta, callback) => {
    // Simulamos un retraso de lectura
    setTimeout(() => {
        const contenido = `Contenido del archivo ${ruta}`;
        const error = null; // En caso de error, aquí iría el objeto error
        callback(error, contenido);
    }, 1000);
};

// Uso del callback
leerArchivo("/var/log/syslog", (err, data) => {
    if (err) {
        console.error("Error:", err);
        return;
    }
    console.log("Contenido:", data);
});

6.2 Promesas

// Ejemplo laboral: verificar conectividad a servidores
const verificarServidor = (servidor) => {
    return new Promise((resolve, reject) => {
        // Simulamos una verificación de red con tiempo variable
        const tiempoRespuesta = Math.random() * 2000;
        const exito = tiempoRespuesta < 1500; // 75% de probabilidad de éxito
        
        setTimeout(() => {
            if (exito) {
                resolve({
                    servidor,
                    estado: "online",
                    tiempoRespuesta: tiempoRespuesta.toFixed(2)
                });
            } else {
                reject(new Error(`Timeout en servidor ${servidor}`));
            }
        }, tiempoRespuesta);
    });
};

// Uso de la promesa
verificarServidor("web01.example.com")
    .then(resultado => {
        console.log("Estado:", resultado.estado);
        console.log("Tiempo de respuesta:", resultado.tiempoRespuesta, "ms");
    })
    .catch(error => {
        console.error("Error:", error.message);
    });

6.3 Async/Await

// Ejemplo avanzado: monitoreo de servidores
const servidores = ["web01", "db01", "app01", "cache01"];

async function monitorearServidores() {
    try {
        // Usamos Promise.all para ejecutar verificaciones en paralelo
        const resultados = await Promise.all(
            servidores.map(servidor => verificarServidor(servidor))
        );
        
        console.log("=== Reporte de estado ===");
        resultados.forEach(({ servidor, estado, tiempoRespuesta }) => {
            console.log(
                `${servidor.padEnd(8)} | ${estado.padEnd(6)} | ${tiempoRespuesta.padStart(6)} ms`
            );
        });
        
        const todosOnline = resultados.every(r => r.estado === "online");
        return todosOnline ? "Todos los servidores operativos" : "Problemas detectados";
    } catch (error) {
        console.error("Error en el monitoreo:", error);
        return "Fallo en el monitoreo";
    }
}

// Ejecutar la función asíncrona
monitorearServidores().then(resumen => console.log(resumen));

7. Módulos en JavaScript

7.1 Módulos ES6

// config.js - Módulo que exporta configuración
const config = {
    entorno: process.env.NODE_ENV || "development",
    servidor: {
        host: "0.0.0.0",
        port: 3000
    },
    db: {
        host: "localhost",
        nombre: "app_db"
    }
};

export default config;

// logger.js - Módulo con exportaciones nombradas
const logInfo = (mensaje) => {
    console.log(`[INFO] ${new Date().toISOString()} - ${mensaje}`);
};

const logError = (mensaje) => {
    console.error(`[ERROR] ${new Date().toISOString()} - ${mensaje}`);
};

export { logInfo, logError };

// app.js - Módulo que importa otros módulos
import config from './config.js';
import { logInfo, logError } from './logger.js';

logInfo(`Iniciando aplicación en entorno ${config.entorno}`);
logInfo(`Servidor escuchando en ${config.servidor.host}:${config.servidor.port}`);

7.2 Módulos CommonJS (Node.js)

// Ejemplo práctico: script de backup en Linux
// backup.js
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

const crearBackup = (directorioOrigen, directorioDestino) => {
    const fecha = new Date().toISOString().replace(/[:.]/g, '-');
    const nombreArchivo = `backup-${fecha}.tar.gz`;
    const rutaCompleta = path.join(directorioDestino, nombreArchivo);
    
    return new Promise((resolve, reject) => {
        const comando = `tar -czf ${rutaCompleta} -C ${directorioOrigen} .`;
        
        exec(comando, (error, stdout, stderr) => {
            if (error) {
                reject(new Error(`Error al crear backup: ${stderr}`));
                return;
            }
            
            resolve({
                mensaje: "Backup creado exitosamente",
                archivo: nombreArchivo,
                ruta: rutaCompleta,
                tamaño: fs.statSync(rutaCompleta).size()
            });
        });
    });
};

// Exportamos la función para que pueda ser usada por otros módulos
module.exports = { crearBackup };

// Uso en terminal Linux:
// node -e "const { crearBackup } = require('./backup'); crearBackup('/var/www', '/backups').then(console.log).catch(console.error);"

8. Manejo de Errores

8.1 Try/Catch

async function procesarDatos(datos) {
    try {
        // Validación de entrada
        if (!datos || !Array.isArray(datos)) {
            throw new Error("Datos de entrada no válidos");
        }
        
        // Procesamiento que podría fallar
        const resultado = datos.map(item => {
            if (!item.id) {
                throw new Error("Item sin ID");
            }
            return { ...item, procesado: true };
        });
        
        return resultado;
    } catch (error) {
        console.error("Error en procesarDatos:", error.message);
        
        // Podemos lanzar un error personalizado
        throw new Error(`Fallo en el procesamiento: ${error.message}`);
    }
}

// Uso con manejo de errores
async function ejecutarProcesamiento() {
    try {
        const datos = [{ id: 1 }, { id: 2 }, {}]; // Datos con error
        const resultado = await procesarDatos(datos);
        console.log(resultado);
    } catch (error) {
        console.error("Error en ejecutarProcesamiento:", error.message);
        // En una aplicación real, podríamos registrar el error en un sistema de monitoreo
    }
}

ejecutarProcesamiento();

8.2 Errores personalizados

// Clase de error personalizado para la aplicación
class AppError extends Error {
    constructor(mensaje, codigo = "APP_ERROR", detalles = {}) {
        super(mensaje);
        this.name = "AppError";
        this.codigo = codigo;
        this.detalles = detalles;
        this.fecha = new Date();
    }
    
    toJSON() {
        return {
            error: this.name,
            codigo: this.codigo,
            mensaje: this.message,
            detalles: this.detalles,
            fecha: this.fecha.toISOString()
        };
    }
}

// Ejemplo de uso en una API
function obtenerUsuario(id) {
    if (!id) {
        throw new AppError("ID de usuario requerido", "VALIDATION_ERROR", {
            campo: "id",
            tipo: "requerido"
        });
    }
    
    // Simulamos que el usuario no existe
    throw new AppError("Usuario no encontrado", "NOT_FOUND", { id });
}

// Manejo del error
try {
    obtenerUsuario();
} catch (error) {
    if (error instanceof AppError) {
        console.error("Error de aplicación:", error.toJSON());
    } else {
        console.error("Error inesperado:", error);
    }
}

9. Trabajo con el Sistema de Archivos en Node.js

9.1 Lectura y escritura de archivos

// Ejemplo avanzado: procesamiento de logs en Linux
const fs = require('fs').promises;
const path = require('path');

async function analizarLogs(rutaLog) {
    try {
        // Verificar si el archivo existe
        try {
            await fs.access(rutaLog);
        } catch {
            throw new Error(`Archivo de log no encontrado: ${rutaLog}`);
        }
        
        // Leer el archivo
        const datos = await fs.readFile(rutaLog, 'utf-8');
        
        // Procesar las líneas
        const lineas = datos.split('\n').filter(linea => linea.trim() !== '');
        
        // Analizar tipos de mensajes
        const resumen = {
            total: lineas.length,
            errores: lineas.filter(linea => linea.includes('ERROR')).length,
            advertencias: lineas.filter(linea => linea.includes('WARN')).length,
            informativos: lineas.filter(linea => linea.includes('INFO')).length
        };
        
        // Generar reporte
        const nombreReporte = `reporte-${path.basename(rutaLog)}.json`;
        const rutaReporte = path.join(path.dirname(rutaLog), nombreReporte);
        
        await fs.writeFile(rutaReporte, JSON.stringify(resumen, null, 2));
        
        return {
            archivoLog: rutaLog,
            archivoReporte: rutaReporte,
            resumen
        };
    } catch (error) {
        console.error("Error al analizar logs:", error.message);
        throw error; // Re-lanzamos el error para que lo maneje el llamador
    }
}

// Uso en terminal Linux:
// node -e "const { analizarLogs } = require('./analizador'); analizarLogs('/var/log/apache2/error.log').then(console.log).catch(console.error);"

9.2 Monitorización de archivos

// Ejemplo: monitorear cambios en un directorio de configuración
const fs = require('fs');
const path = require('path');

class ConfigWatcher {
    constructor(directorio) {
        this.directorio = directorio;
        this.archivos = new Map();
        this.watchers = new Map();
        this.iniciar();
    }
    
    iniciar() {
        console.log(`Monitoreando directorio: ${this.directorio}`);
        
        // Leer directorio inicial
        fs.readdir(this.directorio, (err, archivos) => {
            if (err) {
                console.error("Error al leer directorio:", err);
                return;
            }
            
            archivos.forEach(archivo => {
                this.agregarArchivo(archivo);
            });
        });
        
        // Configurar watcher para el directorio
        this.dirWatcher = fs.watch(this.directorio, (eventType, filename) => {
            if (filename) {
                if (eventType === 'rename') {
                    // Verificar si el archivo fue creado o eliminado
                    fs.stat(path.join(this.directorio, filename), (err, stats) => {
                        if (err) {
                            // Archivo eliminado
                            this.eliminarArchivo(filename);
                        } else {
                            // Archivo creado
                            this.agregarArchivo(filename);
                        }
                    });
                }
            }
        });
    }
    
    agregarArchivo(nombreArchivo) {
        const rutaCompleta = path.join(this.directorio, nombreArchivo);
        
        // Solo monitorear archivos (no directorios) con extensión .conf
        if (!nombreArchivo.endsWith('.conf')) return;
        
        // Obtener contenido inicial
        fs.readFile(rutaCompleta, 'utf8', (err, contenido) => {
            if (err) {
                console.error(`Error al leer ${nombreArchivo}:`, err);
                return;
            }
            
            this.archivos.set(nombreArchivo, contenido);
            console.log(`Archivo agregado: ${nombreArchivo}`);
            
            // Configurar watcher para este archivo
            const watcher = fs.watch(rutaCompleta, (eventType) => {
                if (eventType === 'change') {
                    fs.readFile(rutaCompleta, 'utf8', (err, nuevoContenido) => {
                        if (err) {
                            console.error(`Error al leer cambios en ${nombreArchivo}:`, err);
                            return;
                        }
                        
                        const contenidoAnterior = this.archivos.get(nombreArchivo);
                        if (contenidoAnterior !== nuevoContenido) {
                            console.log(`Archivo modificado: ${nombreArchivo}`);
                            this.archivos.set(nombreArchivo, nuevoContenido);
                            this.emitirEvento('cambio', nombreArchivo, nuevoContenido);
                        }
                    });
                }
            });
            
            this.watchers.set(nombreArchivo, watcher);
        });
    }
    
    eliminarArchivo(nombreArchivo) {
        if (this.archivos.has(nombreArchivo)) {
            const watcher = this.watchers.get(nombreArchivo);
            if (watcher) watcher.close();
            
            this.archivos.delete(nombreArchivo);
            this.watchers.delete(nombreArchivo);
            console.log(`Archivo eliminado: ${nombreArchivo}`);
            this.emitirEvento('eliminado', nombreArchivo);
        }
    }
    
    emitirEvento(evento, ...args) {
        // En una implementación real, usaríamos EventEmitter
        console.log(`Evento: ${evento}`, args);
    }
    
    detener() {
        this.watchers.forEach(watcher => watcher.close());
        if (this.dirWatcher) this.dirWatcher.close();
        console.log("Monitoreo detenido");
    }
}

// Uso en producción:
// const watcher = new ConfigWatcher('/etc/nginx/conf.d');
// Para detener: watcher.detener();

10. Buenas Prácticas y Patrones

10.1 Estructura de proyectos

/*
Estructura recomendada para un proyecto Node.js:

mi-proyecto/
├── src/
│   ├── controllers/    # Controladores de la aplicación
│   ├── models/         # Modelos de datos
│   ├── services/       # Lógica de negocio
│   ├── routes/         # Definición de rutas
│   ├── middlewares/    # Middlewares
│   ├── utils/          # Utilidades compartidas
│   ├── config/         # Configuración
│   └── app.js          # Punto de entrada
├── tests/              # Pruebas
├── scripts/            # Scripts utilitarios (ej: despliegue, migraciones)
├── .env                # Variables de entorno (no versionado)
├── package.json
└── README.md
*/

// Ejemplo de archivo app.js estructurado
const express = require('express');
const config = require('./config');
const logger = require('./utils/logger');

// Inicialización
const app = express();

// Middlewares
app.use(express.json());
app.use(require('./middlewares/requestLogger'));

// Rutas
app.use('/api', require('./routes/api'));

// Manejo de errores
app.use(require('./middlewares/errorHandler'));

// Iniciar servidor
app.listen(config.PORT, () => {
    logger.info(`Servidor escuchando en puerto ${config.PORT}`);
});

10.2 Patrones comunes

// Patrón Factory: creación de objetos complejos
class Logger {
    constructor(tipo = 'console') {
        this.tipo = tipo;
    }
    
    log(mensaje) {
        throw new Error('Método log debe ser implementado');
    }
}

class ConsoleLogger extends Logger {
    log(mensaje) {
        console.log(`[CONSOLE] ${mensaje}`);
    }
}

class FileLogger extends Logger {
    constructor() {
        super('file');
        this.fs = require('fs');
        this.path = './logs.txt';
    }
    
    log(mensaje) {
        this.fs.appendFileSync(
            this.path, 
            `[FILE] ${new Date().toISOString()} - ${mensaje}\n`
        );
    }
}

// Factory function
function crearLogger(tipo) {
    switch (tipo) {
        case 'console':
            return new ConsoleLogger();
        case 'file':
            return new FileLogger();
        default:
            throw new Error(`Tipo de logger desconocido: ${tipo}`);
    }
}

// Uso en producción
const logger = crearLogger(process.env.LOGGER_TYPE || 'console');
logger.log("Este es un mensaje de prueba");

11. Pruebas y Depuración

11.1 Pruebas unitarias con Jest

// Instalación en Linux:
// npm install --save-dev jest

// Ejemplo: pruebas para una función de utilidad
// stringUtils.js
function capitalizar(str) {
    if (typeof str !== 'string') {
        throw new Error('Se esperaba una cadena de texto');
    }
    return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}

function formatearNombreCompleto(nombre, apellido) {
    if (!nombre || !apellido) {
        throw new Error('Nombre y apellido son requeridos');
    }
    return `${capitalizar(nombre)} ${capitalizar(apellido)}`;
}

module.exports = { capitalizar, formatearNombreCompleto };

// stringUtils.test.js
const { capitalizar, formatearNombreCompleto } = require('./stringUtils');

describe('capitalizar', () => {
    test('capitaliza correctamente una cadena', () => {
        expect(capitalizar('hola')).toBe('Hola');
        expect(capitalizar('MUNDO')).toBe('Mundo');
    });
    
    test('lanza error con entrada no string', () => {
        expect(() => capitalizar(123)).toThrow('Se esperaba una cadena de texto');
    });
});

describe('formatearNombreCompleto', () => {
    test('formatea correctamente nombre y apellido', () => {
        expect(formatearNombreCompleto('juan', 'perez')).toBe('Juan Perez');
        expect(formatearNombreCompleto('MARIA', 'GOMEZ')).toBe('Maria Gomez');
    });
    
    test('lanza error con parámetros faltantes', () => {
        expect(() => formatearNombreCompleto('Juan')).toThrow('Nombre y apellido son requeridos');
        expect(() => formatearNombreCompleto()).toThrow();
    });
});

// Ejecutar pruebas:
// npx jest

11.2 Depuración en Node.js

// Ejemplo de código para depurar
function procesarDatos(datos) {
    // Punto de ruptura (breakpoint) se puede agregar aquí
    const resultado = datos
        .map(item => {
            if (!item.id) {
                console.warn('Item sin ID:', item);
                return null;
            }
            return { ...item, procesado: true };
        })
        .filter(item => item !== null);
    
    return resultado;
}

// Métodos para depurar:
// 1. Usando 'debugger' en el código
// 2. Ejecutando Node.js en modo inspect:
//    node --inspect-brk script.js
//    (luego conectar con Chrome DevTools en chrome://inspect)
// 3. Usando el inspector integrado en VS Code

// Ejemplo de datos para probar
const datos = [
    { id: 1, valor: "A" },
    { valor: "B" }, // Sin ID
    { id: 2, valor: "C" }
];

// Ejecutar la función
const resultado = procesarDatos(datos);
console.log(resultado);

12. Seguridad en JavaScript

12.1 Buenas prácticas de seguridad

Importante: La seguridad es crítica en aplicaciones JavaScript, especialmente del lado del servidor.

// Ejemplo: validación y sanitización de entrada
const validator = require('validator');
const xss = require('xss');

function validarYSanitizarUsuario(datosUsuario) {
    const errores = [];
    const usuarioSanitizado = {};
    
    // Validar y sanitizar nombre
    if (!datosUsuario.nombre || !validator.isLength(datosUsuario.nombre, { min: 2, max: 50 })) {
        errores.push('Nombre debe tener entre 2 y 50 caracteres');
    } else {
        usuarioSanitizado.nombre = xss(datosUsuario.nombre.trim());
    }
    
    // Validar y sanitizar email
    if (!validator.isEmail(datosUsuario.email)) {
        errores.push('Email no válido');
    } else {
        usuarioSanitizado.email = validator.normalizeEmail(datosUsuario.email);
    }
    
    // Validar y sanitizar contraseña
    if (!validator.isStrongPassword(datosUsuario.password, { 
        minLength: 8, 
        minLowercase: 1, 
        minUppercase: 1, 
        minNumbers: 1, 
        minSymbols: 1 
    })) {
        errores.push('La contraseña debe tener al menos 8 caracteres, incluyendo mayúsculas, minúsculas, números y símbolos');
    } else {
        // Nunca guardes contraseñas en texto plano en logs o respuestas
        usuarioSanitizado.password = '***'; // En realidad, aquí debería ir el hash
    }
    
    if (errores.length > 0) {
        throw new Error(`Errores de validación: ${errores.join(', ')}`);
    }
    
    return usuarioSanitizado;
}

// Uso:
try {
    const usuario = {
        nombre: '<script>alert("xss")</script> Juan',
        email: '  JUAN@Example.COM  ',
        password: 'Passw0rd!'
    };
    
    const usuarioSanitizado = validarYSanitizarUsuario(usuario);
    console.log('Usuario sanitizado:', usuarioSanitizado);
} catch (error) {
    console.error(error.message);
}

12.2 Configuración segura en Linux

// Ejemplo: configuración segura para una aplicación Node.js en producción
// config/security.js
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

// Configuración de Helmet (seguridad HTTP)
const helmetConfig = helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "'unsafe-inline'", "cdn.example.com"],
            styleSrc: ["'self'", "'unsafe-inline'", "fonts.googleapis.com"],
            imgSrc: ["'self'", "data:", "cdn.example.com"],
            connectSrc: ["'self'", "api.example.com"]
        }
    },
    hsts: {
        maxAge: 31536000, // 1 año en segundos
        includeSubDomains: true,
        preload: true
    },
    referrerPolicy: { policy: 'same-origin' }
});

// Limitador de tasa para prevenir ataques de fuerza bruta
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutos
    max: 100, // límite de 100 peticiones por IP
    message: 'Demasiadas peticiones desde esta IP, por favor intenta de nuevo más tarde'
});

// Configuración de CORS segura
const corsOptions = {
    origin: process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : [],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
    optionsSuccessStatus: 200
};

module.exports = { helmetConfig, limiter, corsOptions };

// Uso en app.js:
// app.use(security.helmetConfig);
// app.use('/api/', security.limiter);
// app.use(cors(security.corsOptions));

// Configuración recomendada en Linux:
// 1. Ejecutar Node.js como usuario no root
// 2. Usar un proxy inverso como Nginx
// 3. Configurar firewall adecuadamente
// 4. Mantener el sistema y las dependencias actualizadas

13. Despliegue en Producción

13.1 Configuración para producción

// Ejemplo: configuración de entorno
// config.js
const requireEnv = (name) => {
    if (!process.env[name]) {
        throw new Error(`Variable de entorno requerida: ${name}`);
    }
    return process.env[name];
};

module.exports = {
    entorno: process.env.NODE_ENV || 'development',
    servidor: {
        host: process.env.HOST || '0.0.0.0',
        port: parseInt(process.env.PORT || '3000', 10)
    },
    db: {
        host: requireEnv('DB_HOST'),
        puerto: requireEnv('DB_PORT'),
        nombre: requireEnv('DB_NAME'),
        usuario: requireEnv('DB_USER'),
        password: requireEnv('DB_PASSWORD')
    },
    jwt: {
        secreto: requireEnv('JWT_SECRET'),
        expiracion: '7d'
    }
};

// Ejemplo de .env (no versionado)
/*
NODE_ENV=production
HOST=0.0.0.0
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mi_app_prod
DB_USER=mi_app_user
DB_PASSWORD=contraseñaSegura123!
JWT_SECRET=miSuperSecretoMuySeguro
*/

13.2 Uso de PM2 para producción

npm install -g pm2
// Archivo de configuración de PM2 (ecosystem.config.js)
module.exports = {
    apps: [{
        name: 'mi-aplicacion',
        script: './src/app.js',
        instances: 'max', // Usar todos los núcleos CPU
        autorestart: true,
        watch: false,
        max_memory_restart: '1G',
        env: {
            NODE_ENV: 'development'
        },
        env_production: {
            NODE_ENV: 'production',
            PORT: 3000
        }
    }]
};

// Comandos útiles de PM2:
// Iniciar aplicación en producción:
// pm2 start ecosystem.config.js --env production

// Monitorear aplicaciones:
// pm2 monit

// Listar aplicaciones:
// pm2 list

// Ver logs:
// pm2 logs

// Reiniciar aplicación:
// pm2 restart mi-aplicacion

// Detener aplicación:
// pm2 stop mi-aplicacion

// Configurar inicio automático en Linux:
// pm2 startup
// pm2 save

14. Recursos Adicionales

14.1 Librerías útiles

14.2 Herramientas de desarrollo

Este manual cubre los conceptos fundamentales y avanzados de JavaScript con un enfoque práctico para entornos laborales y Linux. Para profundizar en cada tema, se recomienda consultar la documentación oficial y practicar con proyectos reales.