EN
$ cat ~/articles/tech-catalog-npm-package.md ← volver a artículos

Construyes un sistema de gestión de CVs donde los candidatos escriben tecnologías que conocen. Un usuario escribe "Reactjs", otro "React.js", otro "react", otro "ReactJS". Todos se refieren a la misma tecnología pero tu base de datos tiene cuatro entradas diferentes. Cuando filtras por React, pierdes el 75% de candidatos porque usaron una variante del nombre.

Desarrollas una plataforma de matching entre proyectos y desarrolladores. Necesitas que el usuario seleccione su stack tecnológico de una lista. Buscas una librería que tenga todas las tecnologías actualizadas: frameworks modernos, bases de datos, herramientas, lenguajes. No existe. Terminas creando un JSON manual que se desactualiza en tres meses.

Implementas un dashboard que muestra estadísticas de tecnologías usadas en tu empresa. Tienes que agrupar frameworks, separar librerías, identificar stacks completos. El código se convierte en un switch gigante con cientos de casos hardcodeados. Añadir una tecnología nueva requiere modificar cinco archivos diferentes.

Estos problemas se repiten en múltiples dominios: sistemas de hiring, portfolios, plataformas educativas, herramientas de análisis técnico. La solución siempre es la misma: un catálogo estructurado con API que simplifique búsqueda, validación y gestión de datos tecnológicos.

Por qué creé este paquete

Necesitaba un catálogo de tecnologías para un proyecto de análisis de perfiles técnicos. Los requisitos eran específicos: búsqueda tolerante a errores tipográficos, capacidad de filtrar por categorías, soporte para stacks tecnológicos completos, y validación estricta de datos. No encontré ningún paquete que cumpliera estos requisitos simultáneamente.

Las alternativas existentes tenían limitaciones concretas. Algunos paquetes solo incluían frameworks JavaScript sin bases de datos ni herramientas. Otros no soportaban búsqueda fuzzy, obligando a implementar matching exacto. Los que incluían búsqueda avanzada arrastraban dependencias pesadas como Fuse.js o Lunr. Ninguno proporcionaba validadores para verificar integridad de datos.

La decisión fue construir un catálogo desde cero con tres principios: cobertura completa de tecnologías modernas, API modular con funciones específicas para cada caso de uso, y cero dependencias en runtime para minimizar tamaño de bundle.

El resultado es @sparring/tech-catalog: 1094 tecnologías organizadas en 8 categorías con API completa para búsqueda, filtrado, validación y análisis. Búsqueda fuzzy implementada con distancia de Levenshtein sin dependencias externas. Soporte nativo para stacks tecnológicos con sus componentes. Tipos TypeScript completos con type guards. Tree-shakeable para importar solo lo necesario.

La instalación es estándar:

npm install @sparring/tech-catalog

El paquete exporta módulos separados para diferentes funcionalidades. El módulo core contiene funciones básicas de acceso a datos. El módulo search implementa búsqueda fuzzy y autocompletado. El módulo filters proporciona filtrado avanzado. El módulo validators tiene funciones de validación.

import { getTechnologies, getTechByName } from '@sparring/tech-catalog';
import { searchTech, autocomplete } from '@sparring/tech-catalog/search';
import { filterTechnologies } from '@sparring/tech-catalog/filters';
import { validateTechnology } from '@sparring/tech-catalog/validators';

Esta modularidad permite tree-shaking. Si solo usas getTechnologies(), el bundler elimina todo el código de búsqueda fuzzy, filtros y validadores del bundle final.

El catálogo organiza tecnologías en 8 categorías: Lenguaje (JavaScript, Python, Rust), Framework (React, Vue, Django), Librería (Lodash, Axios, NumPy), Base de datos (PostgreSQL, MongoDB, Redis), Servidor (Nginx, Apache, Tomcat), Herramienta (Docker, Webpack, ESLint), Plataforma (AWS, Vercel, Heroku), Stack (MERN, LAMP, T3).

La estructura de datos es simple. Una tecnología básica tiene nombre y tipo:

{
  nombre: "React",
  tipo: "Framework"
}

Un stack incluye array de componentes:

{
  nombre: "MERN",
  tipo: "Stack",
  componentes: [
    { nombre: "MongoDB", tipo: "Database" },
    { nombre: "Express", tipo: "Framework" },
    { nombre: "React", tipo: "Framework" },
    { nombre: "Node.js", tipo: "Platform" }
  ]
}

Búsqueda básica y acceso a datos

La función más directa es getTechByName() que busca por nombre exacto sin distinguir mayúsculas:

import { getTechByName } from '@sparring/tech-catalog';

const react = getTechByName('React');
console.log(react); // { nombre: 'React', tipo: 'Framework' }

const alsoReact = getTechByName('react'); // Mismo resultado
const alsoReact2 = getTechByName('REACT'); // Mismo resultado

Esta función usa normalización interna que convierte a minúsculas antes de comparar. Es útil cuando sabes exactamente qué tecnología buscas y solo necesitas verificar que existe o recuperar su tipo.

Si necesitas case-sensitive por alguna razón específica, existe getTechByNameStrict():

import { getTechByNameStrict } from '@sparring/tech-catalog';

const found = getTechByNameStrict('React');     // Encuentra
const notFound = getTechByNameStrict('react');  // undefined

Para búsquedas parciales donde el usuario solo escribió parte del nombre:

import { getTechsByPartialName } from '@sparring/tech-catalog';

const results = getTechsByPartialName('Script');
// Retorna: JavaScript, TypeScript, CoffeeScript, etc.

Filtrar por categoría es común en UIs donde el usuario selecciona tipo de tecnología:

import { getTechsByType } from '@sparring/tech-catalog';

const frameworks = getTechsByType('Framework');
// Retorna todos los frameworks: React, Vue, Angular, etc.

const languages = getTechsByType('Lenguaje');
// Retorna todos los lenguajes: JavaScript, Python, Rust, etc.

Si necesitas múltiples categorías simultáneamente:

import { getTechsByTypes } from '@sparring/tech-catalog';

const frontendTechs = getTechsByTypes(['Framework', 'Librería']);
// Retorna frameworks y librerías combinados

Búsqueda fuzzy: tolerancia a errores tipográficos

El problema real aparece cuando usuarios cometen errores tipográficos. Un desarrollador escribe "Reactjs" en su CV. Otro escribe "React.js". Otro comete un typo y escribe "Raect". La búsqueda exacta falla en los tres casos.

La función searchTech() implementa búsqueda fuzzy usando distancia de Levenshtein. Esta métrica calcula cuántas operaciones (inserción, eliminación, sustitución de caracteres) se necesitan para transformar una cadena en otra. Menor distancia significa mayor similitud.

import { searchTech } from '@sparring/tech-catalog/search';

const results = searchTech('recat');

results.forEach(result => {
  console.log(`${result.technology.nombre}: ${(result.score * 100).toFixed(0)}% match`);
});

// Salida:
// React: 83% match
// Preact: 67% match

La función retorna array ordenado por score de similitud descendente. El score es un número entre 0 y 1 donde 1 es match perfecto y valores cercanos a 0 indican poca similitud.

La estructura completa del resultado incluye la tecnología encontrada, el score, y qué campo matcheó:

{
  technology: { nombre: "React", tipo: "Framework" },
  score: 0.83,
  matches: ["nombre"]
}

Las opciones de búsqueda permiten afinar el comportamiento:

const results = searchTech('react', {
  fuzzy: true,              // Activar fuzzy matching (default: true)
  caseSensitive: false,     // Case-insensitive (default: false)
  maxResults: 10,           // Máximo de resultados (default: 20)
  categories: ['Framework', 'Librería']  // Filtrar por tipos
});

Desactivar fuzzy matching convierte la búsqueda en substring matching exacto. Esto es útil cuando sabes que el usuario escribió correctamente pero solo una parte del nombre:

const exact = searchTech('React', { fuzzy: false });
// Solo retorna tecnologías cuyo nombre contiene exactamente "React"

El parámetro categories combina búsqueda con filtrado de tipos. Buscas frameworks que contengan "js" en el nombre:

const jsFrameworks = searchTech('js', {
  categories: ['Framework'],
  maxResults: 5
});

La implementación de Levenshtein está optimizada para strings cortos como nombres de tecnologías. Usa programación dinámica con matriz de distancias. Para 1094 tecnologías con nombres promedio de 10 caracteres, la búsqueda completa toma menos de 10ms en hardware moderno.

Autocompletado para inputs de usuario

Implementar autocompletado en un input de tecnologías es común en formularios. El usuario escribe las primeras letras y necesitas mostrar sugerencias.

La función autocomplete() está optimizada para este caso:

import { autocomplete } from '@sparring/tech-catalog/search';

function handleInputChange(input: string) {
  const suggestions = autocomplete(input, 5);

  return suggestions.map(tech => ({
    label: tech.nombre,
    category: tech.tipo
  }));
}

// Usuario escribe "reac"
const suggestions = handleInputChange('reac');
// Retorna: [
//   { label: 'React', category: 'Framework' },
//   { label: 'React Native', category: 'Framework' },
//   { label: 'Preact', category: 'Framework' }
// ]

El segundo parámetro limita el número de sugerencias. Para UIs, 5-10 sugerencias es suficiente. Más resultados degradan UX porque el usuario tiene que escanear demasiadas opciones.

Puedes filtrar sugerencias por tipo si el contexto lo requiere:

const frameworkSuggestions = autocomplete('reac', 5, ['Framework']);
// Solo frameworks, excluye librerías o herramientas

La diferencia entre autocomplete() y searchTech() es que autocomplete prioriza prefijos. Si el input matchea el inicio del nombre, ese resultado recibe score más alto. Esto produce sugerencias más intuitivas para autocompletado.

// Usuario escribe "vue"
const results = autocomplete('vue', 3);
// Prioriza: Vue.js, Vuetify, Vuex
// Sobre: Nuxt (contiene 'ue' pero no empieza con 'vue')

Filtrado avanzado multi-criterio

Cuando construyes UIs complejas con múltiples filtros, necesitas combinar criterios. La función filterTechnologies() acepta objeto con todos los filtros:

import { filterTechnologies } from '@sparring/tech-catalog/filters';

const results = filterTechnologies({
  types: ['Framework', 'Librería'],
  nameContains: 'React',
  caseSensitive: false,
  excludeStacks: true,
  onlyStacks: false
});

// Retorna: React, React Native, React Query, React Router, etc.
// Excluye stacks que contengan React en sus componentes

El campo types filtra por categorías. Puedes pasar array con múltiples tipos y la función retorna tecnologías que matcheen cualquiera de ellos.

El campo nameContains hace substring matching. caseSensitive controla si distingue mayúsculas.

Los campos excludeStacks y onlyStacks son mutuamente excluyentes. El primero elimina stacks de los resultados, dejando solo tecnologías individuales. El segundo retorna únicamente stacks.

// Solo stacks tecnológicos
const stacks = filterTechnologies({
  onlyStacks: true
});
// Retorna: MERN, MEAN, T3, LAMP, etc.

// Solo tecnologías individuales
const individual = filterTechnologies({
  excludeStacks: true
});
// Retorna: React, Python, PostgreSQL, etc.
// Excluye: MERN, MEAN, T3, etc.

Trabajar con stacks tecnológicos

Un stack es una colección de tecnologías que se usan juntas. MERN stack combina MongoDB, Express, React y Node.js. T3 stack combina TypeScript, tRPC, Tailwind y Next.js.

El catálogo modela stacks como tecnologías de tipo "Stack" con array de componentes. Cada componente tiene nombre y tipo.

Obtener todos los stacks:

import { getStacks } from '@sparring/tech-catalog';

const stacks = getStacks();

stacks.forEach(stack => {
  console.log(`\n${stack.nombre}:`);
  stack.componentes.forEach(comp => {
    console.log(`  - ${comp.nombre} (${comp.tipo})`);
  });
});

// Salida:
// MERN:
//   - MongoDB (Database)
//   - Express (Framework)
//   - React (Framework)
//   - Node.js (Platform)
//
// T3:
//   - TypeScript (Lenguaje)
//   - tRPC (Framework)
//   - Tailwind CSS (Framework)
//   - Next.js (Framework)

Buscar qué stacks incluyen una tecnología específica:

import { getStacksByComponent } from '@sparring/tech-catalog/filters';

const stacksWithReact = getStacksByComponent('React');
// Retorna: MERN, T3, y otros stacks que usan React

Esto es útil cuando un usuario selecciona React y quieres sugerirle stacks completos que incluyan esa tecnología.

Buscar stacks que contengan componentes de un tipo específico:

import { getStacksByComponentType } from '@sparring/tech-catalog/filters';

const stacksWithFrameworks = getStacksByComponentType('Framework');
// Retorna stacks que tienen al menos un framework

Obtener qué tecnologías aparecen en algún stack:

import { getTechnologiesUsedInStacks } from '@sparring/tech-catalog/filters';

const usedInStacks = getTechnologiesUsedInStacks();
// Retorna: React, MongoDB, Express, PostgreSQL, etc.

Y el complementario, tecnologías que no aparecen en ningún stack:

import { getStandaloneTechnologies } from '@sparring/tech-catalog/filters';

const standalone = getStandaloneTechnologies();
// Tecnologías que no forman parte de stacks predefinidos

Estas funciones simplifican análisis de tendencias. Puedes identificar qué tecnologías son comúnmente usadas en stacks versus cuáles son típicamente standalone.

Validación de datos

Si permites que usuarios añadan tecnologías personalizadas o si importas datos de fuentes externas, necesitas validar estructura y consistencia.

La función validateTechnology() verifica que un objeto tiene estructura válida:

import { validateTechnology } from '@sparring/tech-catalog/validators';

const userInput = {
  nombre: 'React',
  tipo: 'Framework'
};

const validation = validateTechnology(userInput);

if (!validation.isValid) {
  console.error('Errores:', validation.errors);
} else {
  console.log('Tecnología válida');
}

El resultado incluye booleano isValid y array de errores descriptivos:

{
  isValid: false,
  errors: [
    'El campo "nombre" es requerido',
    'El campo "tipo" debe ser un tipo válido'
  ]
}

Los errores comunes que detecta: campo nombre vacío, tipo no es una categoría válida, stack sin array de componentes, componentes de stack sin nombre o tipo.

Validar solo el tipo sin revisar el objeto completo:

import { isValidTechnologyType } from '@sparring/tech-catalog/validators';

if (isValidTechnologyType('Framework')) {
  // Tipo válido
}

if (!isValidTechnologyType('InvalidType')) {
  // Tipo inválido
}

Para stacks, existe validador específico que verifica estructura de componentes:

import { validateStack } from '@sparring/tech-catalog/validators';

const stackInput = {
  nombre: 'Custom Stack',
  tipo: 'Stack',
  componentes: [
    { nombre: 'React', tipo: 'Framework' },
    { nombre: 'Node.js', tipo: 'Platform' }
  ]
};

const validation = validateStack(stackInput);

Este validador verifica que el tipo sea "Stack", que exista array de componentes, que cada componente tenga nombre y tipo válidos, y que no haya componentes duplicados.

La función sanitizeName() normaliza nombres que vienen de input de usuario:

import { sanitizeName } from '@sparring/tech-catalog/validators';

const clean = sanitizeName('  React   Native  ');
console.log(clean); // 'React Native'

Elimina espacios en blanco al inicio y final, normaliza espacios múltiples a uno solo, y capitaliza correctamente según convenciones de nombres de tecnologías.

Validar consistencia del catálogo completo:

import { validateCatalog } from '@sparring/tech-catalog/validators';
import { getTechnologies } from '@sparring/tech-catalog';

const result = validateCatalog(getTechnologies());

console.log('Errores:', result.errors);
console.log('Warnings:', result.warnings);

Esta función detecta problemas como nombres duplicados, componentes de stacks que referencian tecnologías inexistentes, tipos inconsistentes, y categorías inválidas.

TypeScript: tipos completos y type guards

El paquete incluye definiciones TypeScript completas. No necesitas instalar @types/ adicionales.

Los tipos principales son:

import type {
  Technology,        // Union de SimpleTechnology | StackTechnology
  SimpleTechnology,  // Tecnología individual
  StackTechnology,   // Stack con componentes
  TechnologyType,    // Enum de categorías
  SearchResult,      // Resultado de búsqueda con score
  FilterCriteria,    // Opciones de filtrado
  ValidationResult   // Resultado de validación
} from '@sparring/tech-catalog';

Technology es union type que puede ser simple o stack. Necesitas narrow el tipo para acceder a componentes:

function processTechnology(tech: Technology) {
  // Error: Property 'componentes' does not exist on type 'SimpleTechnology'
  // console.log(tech.componentes);

  // Correcto: usar type guard
  if ('componentes' in tech) {
    // Tipo narrowed a StackTechnology
    tech.componentes.forEach(comp => {
      console.log(comp.nombre);
    });
  } else {
    // Tipo narrowed a SimpleTechnology
    console.log(tech.nombre);
  }
}

El paquete exporta type guard para simplificar este patrón:

import { isStackTechnology } from '@sparring/tech-catalog';

function processTechnology(tech: Technology) {
  if (isStackTechnology(tech)) {
    // TypeScript infiere StackTechnology
    tech.componentes.forEach(comp => {
      console.log(comp.nombre);
    });
  } else {
    // TypeScript infiere SimpleTechnology
    console.log(tech.nombre);
  }
}

El type guard hace verificación en runtime y TypeScript usa eso para narrow el tipo automáticamente.

TechnologyType es string literal union de todas las categorías válidas:

type TechnologyType =
  | 'Lenguaje'
  | 'Framework'
  | 'Librería'
  | 'Database'
  | 'Servidor'
  | 'Herramienta'
  | 'Plataforma'
  | 'Stack';

function filterByType(type: TechnologyType) {
  // TypeScript garantiza que type es una categoría válida
  return getTechsByType(type);
}

// Error en compile time:
// filterByType('InvalidCategory');

Esto previene errores donde pasas strings inválidos a funciones que esperan categorías específicas.

SearchResult estructura el retorno de búsquedas fuzzy:

interface SearchResult {
  technology: Technology;
  score: number;        // 0-1
  matches: string[];    // Campos que matchearon
}

FilterCriteria tipea las opciones de filtrado:

interface FilterCriteria {
  types?: TechnologyType[];
  nameContains?: string;
  caseSensitive?: boolean;
  excludeStacks?: boolean;
  onlyStacks?: boolean;
}

Todos los campos son opcionales. TypeScript valida que los tipos en el array sean categorías válidas y que los booleanos sean boolean, no any.

Casos de uso reales

El paquete resuelve problemas específicos en múltiples dominios.

Sistema de gestión de CVs: Los candidatos escriben tecnologías en texto libre. Búsqueda fuzzy normaliza variantes. Validación detecta tecnologías inventadas. Filtrado por categoría permite buscar candidatos con experiencia en frameworks específicos versus lenguajes versus herramientas.

import { searchTech, filterTechnologies } from '@sparring/tech-catalog';

// Usuario escribe "reactjs" en su CV
const normalized = searchTech('reactjs', { maxResults: 1 });
const tech = normalized[0]?.technology.nombre; // 'React'

// Buscar candidatos que sepan algún framework frontend
const frontendFrameworks = filterTechnologies({
  types: ['Framework'],
  nameContains: '' // Todos los frameworks
});

Plataforma de matching proyecto-desarrollador: Un proyecto requiere stack MERN. Necesitas encontrar desarrolladores que conozcan MongoDB, Express, React y Node.js. El catálogo proporciona componentes del stack automáticamente.

import { getStacks, getStacksByComponent } from '@sparring/tech-catalog';

const mern = getStacks().find(s => s.nombre === 'MERN');

const requiredTechs = mern.componentes.map(c => c.nombre);
// ['MongoDB', 'Express', 'React', 'Node.js']

// Encontrar qué otros stacks usa el desarrollador si conoce React
const otherStacks = getStacksByComponent('React');

Dashboard de análisis tecnológico: Visualizas qué tecnologías usa tu empresa. Necesitas agrupar por categoría, identificar stacks completos, calcular estadísticas.

import { getStatistics, getTechsByType } from '@sparring/tech-catalog';

const stats = getStatistics();

console.log(`Total tecnologías: ${stats.total}`);
console.log(`Frameworks: ${stats.byCategory.Framework}`);
console.log(`Stacks definidos: ${stats.totalStacks}`);

// Comparar uso de frameworks vs librerías
const frameworks = getTechsByType('Framework');
const libraries = getTechsByType('Librería');

console.log(`Ratio framework/librería: ${frameworks.length / libraries.length}`);

Autocompletado en formularios: Input de tecnologías con sugerencias en tiempo real. Autocompletado proporciona sugerencias mientras el usuario escribe.

import { autocomplete } from '@sparring/tech-catalog/search';

function SearchInput() {
  const [suggestions, setSuggestions] = useState([]);

  const handleChange = (input: string) => {
    if (input.length < 2) {
      setSuggestions([]);
      return;
    }

    const results = autocomplete(input, 8);
    setSuggestions(results);
  };

  return (
    <input
      onChange={(e) => handleChange(e.target.value)}
      placeholder="Buscar tecnología..."
    />
  );
}

Validación de importaciones: Importas datos de tecnologías desde CSV o API externa. Validas que los datos tienen estructura correcta antes de procesarlos.

import { validateTechnology, sanitizeName } from '@sparring/tech-catalog/validators';

function importTechnologies(rawData: any[]) {
  const valid = [];
  const errors = [];

  rawData.forEach((item, index) => {
    // Normalizar nombre
    if (item.nombre) {
      item.nombre = sanitizeName(item.nombre);
    }

    const validation = validateTechnology(item);

    if (validation.isValid) {
      valid.push(item);
    } else {
      errors.push({
        index,
        item,
        errors: validation.errors
      });
    }
  });

  return { valid, errors };
}

El paquete está en NPM como @sparring/tech-catalog. El código fuente está en GitHub en github.com/686f6c61/npm-tech-catalog. La demo interactiva está en npm-tech-catalog.onrender.com donde puedes probar búsquedas, filtros y explorar el catálogo completo.

La implementación completa incluye 1094 tecnologías actualizadas a 2025. El catálogo se mantiene actualizado añadiendo tecnologías nuevas cuando alcanzan adopción significativa en la industria. Las contribuciones son bienvenidas para añadir tecnologías faltantes o corregir categorizaciones.