ES
$ cat ~/articles/tech-catalog-npm-package.md ← back to articles

You build a CV management system where candidates write technologies they know. One user writes "Reactjs", another "React.js", another "react", another "ReactJS". All refer to the same technology but your database has four different entries. When you filter by React, you lose 75% of candidates because they used a name variant.

You develop a matching platform between projects and developers. You need users to select their technology stack from a list. You search for a library that has all updated technologies: modern frameworks, databases, tools, languages. It doesn't exist. You end up creating a manual JSON that becomes outdated in three months.

You implement a dashboard showing statistics of technologies used in your company. You have to group frameworks, separate libraries, identify complete stacks. The code becomes a giant switch with hundreds of hardcoded cases. Adding a new technology requires modifying five different files.

These problems repeat across multiple domains: hiring systems, portfolios, educational platforms, technical analysis tools. The solution is always the same: a structured catalog with API that simplifies search, validation and technology data management.

Why I created this package

I needed a technology catalog for a technical profile analysis project. The requirements were specific: typo-tolerant search, ability to filter by categories, support for complete technology stacks, and strict data validation. I found no package that met these requirements simultaneously.

Existing alternatives had concrete limitations. Some packages only included JavaScript frameworks without databases or tools. Others didn't support fuzzy search, forcing exact matching implementation. Those with advanced search dragged heavy dependencies like Fuse.js or Lunr. None provided validators to verify data integrity.

The decision was to build a catalog from scratch with three principles: complete coverage of modern technologies, modular API with specific functions for each use case, and zero runtime dependencies to minimize bundle size.

The result is @sparring/tech-catalog: 1094 technologies organized in 8 categories with complete API for search, filtering, validation and analysis. Fuzzy search implemented with Levenshtein distance without external dependencies. Native support for technology stacks with their components. Complete TypeScript types with type guards. Tree-shakeable to import only what's needed.

Installation and catalog structure

Installation is standard:

npm install @sparring/tech-catalog

The package exports separated modules for different functionalities. The core module contains basic data access functions. The search module implements fuzzy search and autocomplete. The filters module provides advanced filtering. The validators module has validation functions.

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';

This modularity enables tree-shaking. If you only use getTechnologies(), the bundler eliminates all fuzzy search, filters and validators code from the final bundle.

The catalog organizes technologies into 8 categories: Language (JavaScript, Python, Rust), Framework (React, Vue, Django), Library (Lodash, Axios, NumPy), Database (PostgreSQL, MongoDB, Redis), Server (Nginx, Apache, Tomcat), Tool (Docker, Webpack, ESLint), Platform (AWS, Vercel, Heroku), Stack (MERN, LAMP, T3).

The data structure is simple. A basic technology has name and type:

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

A stack includes component array:

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

Basic search and data access

The most direct function is getTechByName() which searches by exact name case-insensitively:

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

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

const alsoReact = getTechByName('react'); // Same result
const alsoReact2 = getTechByName('REACT'); // Same result

This function uses internal normalization that converts to lowercase before comparing. It's useful when you know exactly which technology you're looking for and only need to verify it exists or retrieve its type.

If you need case-sensitive for some specific reason, getTechByNameStrict() exists:

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

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

For partial searches where the user only wrote part of the name:

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

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

Filtering by category is common in UIs where the user selects technology type:

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

const frameworks = getTechsByType('Framework');
// Returns all frameworks: React, Vue, Angular, etc.

const languages = getTechsByType('Lenguaje');
// Returns all languages: JavaScript, Python, Rust, etc.

If you need multiple categories simultaneously:

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

const frontendTechs = getTechsByTypes(['Framework', 'Librería']);
// Returns frameworks and libraries combined

Fuzzy search: typo tolerance

The real problem appears when users make typos. A developer writes "Reactjs" in their CV. Another writes "React.js". Another makes a typo and writes "Raect". Exact search fails in all three cases.

The searchTech() function implements fuzzy search using Levenshtein distance. This metric calculates how many operations (insertion, deletion, character substitution) are needed to transform one string into another. Lower distance means higher similarity.

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`);
});

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

The function returns array sorted by similarity score descending. The score is a number between 0 and 1 where 1 is perfect match and values near 0 indicate low similarity.

The complete result structure includes the found technology, the score, and which field matched:

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

Search options allow fine-tuning behavior:

const results = searchTech('react', {
  fuzzy: true,              // Enable fuzzy matching (default: true)
  caseSensitive: false,     // Case-insensitive (default: false)
  maxResults: 10,           // Maximum results (default: 20)
  categories: ['Framework', 'Librería']  // Filter by types
});

Disabling fuzzy matching converts search to exact substring matching. This is useful when you know the user typed correctly but only part of the name:

const exact = searchTech('React', { fuzzy: false });
// Only returns technologies whose name contains exactly "React"

The categories parameter combines search with type filtering. You search frameworks containing "js" in the name:

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

The Levenshtein implementation is optimized for short strings like technology names. It uses dynamic programming with distance matrix. For 1094 technologies with average names of 10 characters, complete search takes less than 10ms on modern hardware.

Autocomplete for user inputs

Implementing autocomplete in a technology input is common in forms. The user types the first letters and you need to show suggestions.

The autocomplete() function is optimized for this case:

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
  }));
}

// User types "reac"
const suggestions = handleInputChange('reac');
// Returns: [
//   { label: 'React', category: 'Framework' },
//   { label: 'React Native', category: 'Framework' },
//   { label: 'Preact', category: 'Framework' }
// ]

The second parameter limits the number of suggestions. For UIs, 5-10 suggestions is enough. More results degrade UX because the user has to scan too many options.

You can filter suggestions by type if context requires it:

const frameworkSuggestions = autocomplete('reac', 5, ['Framework']);
// Only frameworks, excludes libraries or tools

The difference between autocomplete() and searchTech() is that autocomplete prioritizes prefixes. If the input matches the beginning of the name, that result receives higher score. This produces more intuitive suggestions for autocomplete.

// User types "vue"
const results = autocomplete('vue', 3);
// Prioritizes: Vue.js, Vuetify, Vuex
// Over: Nuxt (contains 'ue' but doesn't start with 'vue')

Advanced multi-criteria filtering

When you build complex UIs with multiple filters, you need to combine criteria. The filterTechnologies() function accepts object with all filters:

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

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

// Returns: React, React Native, React Query, React Router, etc.
// Excludes stacks containing React in their components

The types field filters by categories. You can pass array with multiple types and the function returns technologies matching any of them.

The nameContains field does substring matching. caseSensitive controls whether it distinguishes uppercase.

The excludeStacks and onlyStacks fields are mutually exclusive. The first removes stacks from results, leaving only individual technologies. The second returns only stacks.

// Only technology stacks
const stacks = filterTechnologies({
  onlyStacks: true
});
// Returns: MERN, MEAN, T3, LAMP, etc.

// Only individual technologies
const individual = filterTechnologies({
  excludeStacks: true
});
// Returns: React, Python, PostgreSQL, etc.
// Excludes: MERN, MEAN, T3, etc.

Working with technology stacks

A stack is a collection of technologies used together. MERN stack combines MongoDB, Express, React and Node.js. T3 stack combines TypeScript, tRPC, Tailwind and Next.js.

The catalog models stacks as "Stack" type technologies with component array. Each component has name and type.

Get all 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})`);
  });
});

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

Search which stacks include a specific technology:

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

const stacksWithReact = getStacksByComponent('React');
// Returns: MERN, T3, and other stacks using React

This is useful when a user selects React and you want to suggest complete stacks including that technology.

Search stacks containing components of a specific type:

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

const stacksWithFrameworks = getStacksByComponentType('Framework');
// Returns stacks having at least one framework

Get which technologies appear in some stack:

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

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

And the complementary, technologies not appearing in any stack:

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

const standalone = getStandaloneTechnologies();
// Technologies not part of predefined stacks

These functions simplify trend analysis. You can identify which technologies are commonly used in stacks versus which are typically standalone.

Data validation

If you allow users to add custom technologies or import data from external sources, you need to validate structure and consistency.

The validateTechnology() function verifies an object has valid structure:

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

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

const validation = validateTechnology(userInput);

if (!validation.isValid) {
  console.error('Errors:', validation.errors);
} else {
  console.log('Valid technology');
}

The result includes isValid boolean and descriptive error array:

{
  isValid: false,
  errors: [
    'Field "nombre" is required',
    'Field "tipo" must be a valid type'
  ]
}

Common errors it detects: empty name field, type is not a valid category, stack without component array, stack components without name or type.

Validate only the type without reviewing the complete object:

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

if (isValidTechnologyType('Framework')) {
  // Valid type
}

if (!isValidTechnologyType('InvalidType')) {
  // Invalid type
}

For stacks, specific validator exists that verifies component structure:

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);

This validator verifies the type is "Stack", component array exists, each component has valid name and type, and there are no duplicate components.

The sanitizeName() function normalizes names coming from user input:

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

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

It removes whitespace at beginning and end, normalizes multiple spaces to single space, and capitalizes correctly according to technology name conventions.

Validate complete catalog consistency:

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

const result = validateCatalog(getTechnologies());

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

This function detects problems like duplicate names, stack components referencing non-existent technologies, inconsistent types, and invalid categories.

TypeScript: complete types and type guards

The package includes complete TypeScript definitions. You don't need to install additional @types/.

Main types are:

import type {
  Technology,        // Union of SimpleTechnology | StackTechnology
  SimpleTechnology,  // Individual technology
  StackTechnology,   // Stack with components
  TechnologyType,    // Category enum
  SearchResult,      // Search result with score
  FilterCriteria,    // Filtering options
  ValidationResult   // Validation result
} from '@sparring/tech-catalog';

Technology is union type that can be simple or stack. You need to narrow the type to access components:

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

  // Correct: use type guard
  if ('componentes' in tech) {
    // Type narrowed to StackTechnology
    tech.componentes.forEach(comp => {
      console.log(comp.nombre);
    });
  } else {
    // Type narrowed to SimpleTechnology
    console.log(tech.nombre);
  }
}

The package exports type guard to simplify this pattern:

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

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

The type guard does runtime verification and TypeScript uses that to narrow the type automatically.

TechnologyType is string literal union of all valid categories:

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

function filterByType(type: TechnologyType) {
  // TypeScript guarantees type is a valid category
  return getTechsByType(type);
}

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

This prevents errors where you pass invalid strings to functions expecting specific categories.

SearchResult structures fuzzy search return:

interface SearchResult {
  technology: Technology;
  score: number;        // 0-1
  matches: string[];    // Matched fields
}

FilterCriteria types filtering options:

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

All fields are optional. TypeScript validates that types in the array are valid categories and booleans are boolean, not any.

Real use cases

The package solves specific problems across multiple domains.

CV management system: Candidates write technologies in free text. Fuzzy search normalizes variants. Validation detects invented technologies. Category filtering allows searching candidates with experience in specific frameworks versus languages versus tools.

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

// User writes "reactjs" in their CV
const normalized = searchTech('reactjs', { maxResults: 1 });
const tech = normalized[0]?.technology.nombre; // 'React'

// Search candidates knowing any frontend framework
const frontendFrameworks = filterTechnologies({
  types: ['Framework'],
  nameContains: '' // All frameworks
});

Project-developer matching platform: A project requires MERN stack. You need to find developers who know MongoDB, Express, React and Node.js. The catalog provides stack components automatically.

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']

// Find what other stacks the developer uses if they know React
const otherStacks = getStacksByComponent('React');

Technology analysis dashboard: You visualize which technologies your company uses. You need to group by category, identify complete stacks, calculate statistics.

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

const stats = getStatistics();

console.log(`Total technologies: ${stats.total}`);
console.log(`Frameworks: ${stats.byCategory.Framework}`);
console.log(`Defined stacks: ${stats.totalStacks}`);

// Compare framework vs library usage
const frameworks = getTechsByType('Framework');
const libraries = getTechsByType('Librería');

console.log(`Framework/library ratio: ${frameworks.length / libraries.length}`);

Form autocomplete: Technology input with real-time suggestions. Autocomplete provides suggestions while the user types.

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="Search technology..."
    />
  );
}

Import validation: You import technology data from CSV or external API. You validate data has correct structure before processing.

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

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

  rawData.forEach((item, index) => {
    // Normalize name
    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 };
}

The package is on NPM as @sparring/tech-catalog. Source code is on GitHub at github.com/686f6c61/npm-tech-catalog. Interactive demo is at npm-tech-catalog.onrender.com where you can try searches, filters and explore the complete catalog.

The complete implementation includes 1094 technologies updated to 2025. The catalog is maintained updated by adding new technologies when they reach significant industry adoption. Contributions are welcome to add missing technologies or correct categorizations.