Introducción a la Extensibilidad de fast-cli
fast-cli es una herramienta de línea de comadnos para evaluar la velocidad de red utilizando fast.com. Este tutorial aborda la creación de extensiones que permiten adaptar salidas y funcionalidades según necesidades específicas.
Razones para Ampliar fast-cli
La extensibilidad ofrece beneficios clave, tales como:
- Formatos de datos personalizados: Generar resultados en CSV, XML, HTML u otros formatos estructurados.
- Integración con sistemas de monitoreo: Enviar métricas a plataformas como Prometheus o Grafana.
- Tareas programadas: Implementar pruebas periódicas para registrar el historial de calidad de red.
- Alertas configurables: Notificar automáticamente cuando la velocidad caiga por debajo de umbrales definidos.
Análisis de la Estructura del Proyecto
Para desarrollar extensiones, es fundamental comprender la arquitectura de fast-cli:
fast-cli/
├── source/
│ ├── cli.tsx # Entrada CLI y análisis de argumentos
│ ├── ui.tsx # Componentes de interfaz de usuario
│ ├── api.tsx # Interacción con la API de fast.com
│ ├── types.ts # Definiciones de tipos
│ └── utilities.ts # Funciones auxiliares
├── package.json # Configuración y dependencias
└── tsconfig.json # Configuración de TypeScript
Tipos de Datos Esenciales
En source/types.ts se define la estructura completa de los datos de medición:
export type RedDatos = {
readonly velocidadDescarga: number;
readonly velocidadSubida: number;
readonly unidadDescarga: UnidadVelocidad;
readonly datosDescargados: number;
readonly unidadSubida: UnidadVelocidad;
readonly datosSubidos: number;
readonly latencia: number;
readonly bloatBuffer: number;
readonly ubicacionUsuario: string;
readonly ubicacionesServidor: string[];
readonly ipUsuario: string;
readonly completado: boolean;
};
Implementación de Formatos de Salida Personalizados
1. Añadir Parámetros CLI Nuevos
En source/cli.tsx, se extienden los flags del comando para soportar formatos alternativos:
flags: {
// Parámetros existentes omitidos por brevedad
csv: {
type: 'boolean',
description: 'Emitir resultados en formato CSV'
},
xml: {
type: 'boolean',
description: 'Emitir resultados en formato XML'
},
html: {
type: 'boolean',
description: 'Emitir resultados en formato HTML'
}
} as const,
2. Crear Módulos de Formateo
Desarrollar source/formatos.ts con funciones para cada formato:
export function aCSV(datos: RedDatos): string {
const fechaHora = new Date().toISOString();
return `${fechaHora};${datos.velocidadDescarga};${datos.unidadDescarga};${datos.velocidadSubida};${datos.unidadSubida};${datos.latencia};${datos.ubicacionUsuario}`;
}
export function aXML(datos: RedDatos): string {
return `<?xml version="1.0" encoding="UTF-8"?>
<prueba_red>
<marca_tiempo>${new Date().toISOString()}</marca_tiempo>
<descarga>
<velocidad>${datos.velocidadDescarga}</velocidad>
<unidad>${datos.unidadDescarga}</unidad>
</descarga>
<subida>
<velocidad>${datos.velocidadSubida}</velocidad>
<unidad>${datos.unidadSubida}</unidad>
</subida>
<latencia>${datos.latencia}</latencia>
<ubicacion>${datos.ubicacionUsuario}</ubicacion>
</prueba_red>`;
}
3. Integración en la Interfaz
Modificar source/ui.tsx para aplicar el formateo según la selección:
import { aCSV, aXML, aHTML } from './formatos.js';
const formatearSalida = (datos: RedDatos, formato: string) => {
switch(formato) {
case 'csv':
return aCSV(datos);
case 'xml':
return aXML(datos);
case 'html':
return aHTML(datos);
case 'json':
return JSON.stringify(datos, null, 2);
default:
return `${datos.velocidadDescarga} ${datos.unidadDescarga}`;
}
};
Casos Prácticos de Mejora
Caso 1: Script de Monitoreo Periódico
Crear monitor.js para ejecutar pruebas regulares y almacenar datos:
#!/usr/bin/env node
import { ejecutar } from 'child_process';
import { agregarArchivo } from 'fs/promises';
import { programar } from 'node-cron';
programar('*/30 * * * *', async () => {
ejecutar('fast --upload --json', async (error, salida) => {
if (error) {
console.error('Fallo en medición:', error);
return;
}
const datos = JSON.parse(salida);
const registro = {
marca_tiempo: new Date().toISOString(),
...datos
};
const lineaCSV = `${registro.marca_tiempo},${registro.velocidadDescarga},${registro.velocidadSubida},${registro.latencia}\n`;
await agregarArchivo('historial-velocidad.csv', lineaCSV);
if (registro.velocidadDescarga < 50) { // Umbral de 50 Mbps
console.warn('⚠️ ¡Velocidad de red por debajo del umbral!');
// Lógica para enviar notificaciones
}
});
});
Caso 2: Integración con Prometheus
Desarrollar un exportador para exponer métricas:
import express from 'express';
import { ejecutar } from 'child_process';
const aplicacion = express();
const puerto = 9100;
aplicacion.get('/metricas', async (solicitud, respuesta) => {
try {
const datosRed = await obtenerDatosVelocidad();
const metricas = `
# HELP velocidad_descarga_red Velocidad de descarga en Mbps
# TYPE velocidad_descarga_red gauge
velocidad_descarga_red ${datosRed.velocidadDescarga}
# HELP velocidad_subida_red Velocidad de subida en Mbps
# TYPE velocidad_subida_red gauge
velocidad_subida_red ${datosRed.velocidadSubida}
# HELP latencia_red_ms Latencia de red en milisegundos
# TYPE latencia_red_ms gauge
latencia_red_ms ${datosRed.latencia}
`;
respuesta.set('Content-Type', 'text/plain');
respuesta.send(metricas);
} catch (error) {
respuesta.status(500).send(error.message);
}
});
async function obtenerDatosVelocidad() {
return new Promise((resolver, rechazar) => {
ejecutar('fast --upload --json', (error, salida) => {
if (error) rechazar(error);
resolver(JSON.parse(salida));
});
});
}
aplicacion.listen(puerto, () => {
console.log(`Exportador Prometheus escuchando en puerto ${puerto}`);
});
Técnicas Avanzadas de Extensión
1. Arquitectura de Sistema de Plugins
Diseñar interfaces para plugins dinámicos:
interface PluginFastCli {
nombre: string;
version: string;
registrar: (cli: any) => void;
}
class GestorPlugins {
private plugins: PluginFastCli[] = [];
registrar(plugin: PluginFastCli) {
this.plugins.push(plugin);
}
aplicarTodos(cli: any) {
this.plugins.forEach(plugin => plugin.registrar(cli));
}
}
2. Servidores de Prueba Personalizados
Adaptar source/api.tsx para permitir servidores alternativos:
export async function medirVelocidad(opciones: {
subida?: boolean;
verboso?: boolean;
urlServidor?: string; // URL de servidor personalizado
} = {}) {
const servidor = opciones.urlServidor || 'https://fast.com';
// Implementación de medición con servidor configurado
}
3. Sistema de Puntuación de Calidad de Red
Calcular un puntaje basado en métricas:
export function calcularPuntajeRed(datos: RedDatos): number {
const puntajeDescarga = Math.min(datos.velocidadDescarga / 100, 1) * 40;
const puntajeSubida = Math.min(datos.velocidadSubida / 50, 1) * 30;
const puntajeLatencia = Math.max(0, 1 - datos.latencia / 100) * 20;
const puntajeEstabilidad = datos.bloatBuffer < 50 ? 10 : 0;
return puntajeDescarga + puntajeSubida + puntajeLatencia + puntajeEstabilidad;
}
Pruebas y Mejores Prácticas de Despliegue
Pruebas Unitarias para Extensiones
Crear pruebas para validar funcionalidades extendidas:
import test from 'ava';
import { aCSV, aXML } from './source/formatos.js';
test('El formateador CSV funciona correctamente', t => {
const datosSimulados = {
velocidadDescarga: 100,
unidadDescarga: 'Mbps',
velocidadSubida: 50,
unidadSubida: 'Mbps',
latencia: 10,
ubicacionUsuario: 'Madrid, ES'
};
const resultado = aCSV(datosSimulados);
t.true(resultado.includes('100'));
t.true(resultado.includes('Mbps'));
t.true(resultado.includes('Madrid'));
});
Construcción y Distribución
Configurar scripts en package.json para manejar extensiones:
{
"scripts": {
"build": "tsc",
"build:extendido": "tsc && node scripts/construir-extensiones.js",
"test:extensiones": "ava test/extensiones.test.js"
}
}
Preguntas Frecuentes
¿Qué conocimientso se requieren para extender fast-cli? Es necesario dominar Node.js, TypeScript, desarrollo de herramientas CLI y principios de medición de red.
¿Cómo mantener compatibilidad con versiones anteriores? Evitar modificaciones en la lógica central, usar mecanismos de plugins o configuraciones para añadir funciones.
¿Cuál es el método para distribuir extensiones? Publicar como paquete npm independiente o compartir mediante repositorios, instalando con npm install.
¿Recomendaciones para optimizar rendimiento? Implementar caché para evitar repeticiones, procesamiento por lotes y operaciones asíncronas para tareas costosas.