← Files Willy SEOARCHIVED FILE

src/rules/technical.ts

7.03 KB · Oct 2, 2026 · 00:34 UTC

↓ Download file

import { AuditCheck, ExtractedPageData } from '../types.js';

export function auditTechnical(data: ExtractedPageData): AuditCheck[] {
  const checks: AuditCheck[] = [];

  // 1. HTTP Status Code
  if (data.statusCode >= 200 && data.statusCode < 300) {
    checks.push({
      id: 'tech-status-200',
      category: 'technical',
      name: 'Código de Estado HTTP',
      status: 'good',
      score: 100,
      weight: 30,
      message: `La página responde con código de estado HTTP ${data.statusCode} OK.`,
    });
  } else if (data.statusCode >= 300 && data.statusCode < 400) {
    checks.push({
      id: 'tech-status-redirect',
      category: 'technical',
      name: 'Código de Estado HTTP',
      status: 'warning',
      score: 70,
      weight: 30,
      message: `La URL genera una redirección (HTTP ${data.statusCode}).`,
      recommendation: 'Asegúrate de que la redirección sea 301 (permanente) y no cree bucles ni cadenas de redirección.',
    });
  } else {
    checks.push({
      id: 'tech-status-error',
      category: 'technical',
      name: 'Código de Estado HTTP',
      status: 'critical',
      score: 0,
      weight: 35,
      message: `La página devuelve un error HTTP ${data.statusCode}.`,
      recommendation: 'Revisa la disponibilidad del servidor o la ruta del recurso solicitado.',
    });
  }

  // 2. Meta Robots (Index / Noindex)
  if (data.metaRobots) {
    if (data.metaRobots.includes('noindex')) {
      checks.push({
        id: 'tech-robots-noindex',
        category: 'technical',
        name: 'Directiva de Indexación (Meta Robots)',
        status: 'critical',
        score: 0,
        weight: 30,
        message: `Detectada directiva "noindex" (${data.metaRobots}). Esta página está bloqueada para los buscadores y NO aparecerá en Google.`,
        recommendation: 'Si deseas posicionar esta página, elimina la etiqueta "noindex".',
        snippet: `<meta name="robots" content="${data.metaRobots}">`,
      });
    } else {
      checks.push({
        id: 'tech-robots-index',
        category: 'technical',
        name: 'Directiva de Indexación (Meta Robots)',
        status: 'good',
        score: 100,
        weight: 20,
        message: `Página indexable. Directiva: "${data.metaRobots}".`,
      });
    }
  } else {
    checks.push({
      id: 'tech-robots-default',
      category: 'technical',
      name: 'Directiva de Indexación (Meta Robots)',
      status: 'good',
      score: 100,
      weight: 20,
      message: 'No hay restricción de indexación (por defecto Google indexará la página: index, follow).',
    });
  }

  // 3. Etiqueta Canonical
  if (!data.canonical) {
    checks.push({
      id: 'tech-canonical-missing',
      category: 'technical',
      name: 'Etiqueta Canonical',
      status: 'warning',
      score: 50,
      weight: 15,
      message: 'No se ha especificado etiqueta <link rel="canonical">.',
      recommendation: 'Añade una etiqueta canonical autorreferencial para prevenir problemas de contenido duplicado (con parámetros URL, www vs non-www, etc.).',
    });
  } else {
    try {
      const pageUrl = new URL(data.url);
      const canUrl = new URL(data.canonical, data.url);

      const isSelfCanonical = pageUrl.origin + pageUrl.pathname === canUrl.origin + canUrl.pathname;

      if (isSelfCanonical) {
        checks.push({
          id: 'tech-canonical-self',
          category: 'technical',
          name: 'Etiqueta Canonical',
          status: 'good',
          score: 100,
          weight: 15,
          message: 'Etiqueta canonical autorreferencial correctamente configurada.',
          snippet: `<link rel="canonical" href="${data.canonical}" />`,
        });
      } else {
        checks.push({
          id: 'tech-canonical-cross',
          category: 'technical',
          name: 'Etiqueta Canonical',
          status: 'info',
          score: 85,
          weight: 15,
          message: `La etiqueta canonical apunta a otra URL: "${data.canonical}".`,
          recommendation: 'Verifica si esta página transfiere intencionadamente su autoridad a la URL canónica indicada.',
          snippet: `<link rel="canonical" href="${data.canonical}" />`,
        });
      }
    } catch {
      checks.push({
        id: 'tech-canonical-invalid',
        category: 'technical',
        name: 'Etiqueta Canonical',
        status: 'warning',
        score: 40,
        weight: 15,
        message: `La URL de la etiqueta canonical es inválida: "${data.canonical}".`,
      });
    }
  }

  // 4. Tiempo de respuesta (TTFB/Server Response)
  if (data.responseTimeMs > 2500) {
    checks.push({
      id: 'tech-response-time-slow',
      category: 'technical',
      name: 'Tiempo de Respuesta del Servidor (TTFB)',
      status: 'critical',
      score: 40,
      weight: 15,
      message: `Tiempo de respuesta elevado: ${data.responseTimeMs} ms.`,
      recommendation: 'Optimiza la caché de servidor, consulta a base de datos o utiliza una CDN (Cloudflare) para reducir el tiempo por debajo de 600ms.',
    });
  } else if (data.responseTimeMs > 1000) {
    checks.push({
      id: 'tech-response-time-medium',
      category: 'technical',
      name: 'Tiempo de Respuesta del Servidor (TTFB)',
      status: 'warning',
      score: 75,
      weight: 15,
      message: `Tiempo de respuesta moderado: ${data.responseTimeMs} ms.`,
      recommendation: 'Recomendable optimizar para servir la respuesta en menos de 600 ms.',
    });
  } else {
    checks.push({
      id: 'tech-response-time-fast',
      category: 'technical',
      name: 'Tiempo de Respuesta del Servidor (TTFB)',
      status: 'good',
      score: 100,
      weight: 15,
      message: `Excelente tiempo de respuesta: ${data.responseTimeMs} ms.`,
    });
  }

  // 5. Viewport Móvil
  if (!data.viewport) {
    checks.push({
      id: 'tech-viewport-missing',
      category: 'technical',
      name: 'Optimización Mobile-Friendly (Viewport)',
      status: 'critical',
      score: 0,
      weight: 15,
      message: 'Falta la metaetiqueta viewport. El sitio no se adaptará correctamente a dispositivos móviles.',
      recommendation: 'Añade `<meta name="viewport" content="width=device-width, initial-scale=1.0">`.',
    });
  } else {
    checks.push({
      id: 'tech-viewport-ok',
      category: 'technical',
      name: 'Optimización Mobile-Friendly (Viewport)',
      status: 'good',
      score: 100,
      weight: 15,
      message: 'Meta viewport configurado correctamente.',
    });
  }

  // 6. Protocolo HTTPS
  if (data.url.startsWith('http://')) {
    checks.push({
      id: 'tech-https-missing',
      category: 'technical',
      name: 'Seguridad HTTPS',
      status: 'critical',
      score: 0,
      weight: 15,
      message: 'La página se sirve mediante protocolo HTTP no seguro.',
      recommendation: 'Instala un certificado SSL/TLS y fuerza la redirección a HTTPS (HSTS).',
    });
  } else {
    checks.push({
      id: 'tech-https-ok',
      category: 'technical',
      name: 'Seguridad HTTPS',
      status: 'good',
      score: 100,
      weight: 10,
      message: 'Protocolo HTTPS seguro y verificado.',
    });
  }

  return checks;
}

SHA-256: cdc83cb535a6d6f35983e60d53d98203a423cd5f81b6f19575ea6090e7ff72de