npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@empleado-juan/commons-backend

v1.1.4

Published

Shared backend code (event bus, license client, middlewares)

Downloads

42

Readme

@empleado-juan/commons-backend

npm version

Paquete de código compartido exclusivo para backends del sistema Empleado Juan Control.

Instalación

npm install @empleado-juan/commons-backend @empleado-juan/commons

Nota: Requiere @empleado-juan/commons como peer dependency.

Características

  • Event Bus Adapters: RabbitMQ y Kafka listos para usar
  • License Client: Cliente de licencias con cache y WebSocket
  • Factory Pattern: Cambio de broker sin modificar código
  • TypeScript: Completamente tipado
  • Production Ready: Manejo de errores, retry, DLQ

Contenido

🚌 Event Bus

Sistema de mensajería con soporte para múltiples brokers.

Interfaces

import type { IEventBus, IEventConsumer } from '@empleado-juan/commons-backend';

Factory Pattern

import { EventBusFactory } from '@empleado-juan/commons-backend';

// Publisher
const publisher = EventBusFactory.createPublisher();
await publisher.connect();
await publisher.publish('my-topic', {
  key: 'user-123',
  value: { name: 'Juan' },
  headers: { 'content-type': 'application/json' }
});

// Consumer
const consumer = EventBusFactory.createConsumer();
await consumer.connect();
await consumer.subscribe(['topic-1', 'topic-2']);
await consumer.start(async (topic, data) => {
  console.log(`Received from ${topic}:`, data);
});

Configuración

El tipo de broker se configura con variables de entorno:

# RabbitMQ (default)
MESSAGE_BROKER=rabbitmq
RABBITMQ_URL=amqp://localhost:5672
RABBITMQ_QUEUE=my-queue

# Kafka
MESSAGE_BROKER=kafka
KAFKA_BROKERS=localhost:9092
KAFKA_GROUP_ID=my-consumer-group

Adapters Directos

Si necesitas más control, puedes usar los adapters directamente:

import {
  RabbitMQPublisher,
  RabbitMQConsumer,
  KafkaPublisher,
  KafkaConsumer
} from '@empleado-juan/commons-backend';

// RabbitMQ
const rabbitmq = new RabbitMQPublisher('amqp://localhost:5672', 'my-exchange');
await rabbitmq.connect();

// Kafka
const kafka = new KafkaPublisher(['localhost:9092'], 'my-client-id');
await kafka.connect();

🔐 License Client

Cliente para validación de licencias con soporte para:

  • ✅ Cache (5 min TTL)
  • ✅ Grace period (24-48 horas)
  • ✅ WebSocket para updates en tiempo real
  • ✅ Heartbeat automático
import { LicenseClient } from '@empleado-juan/commons-backend';
import type { LicenseStatus, HeartbeatMetrics } from '@empleado-juan/commons';

// Crear instancia
const licenseClient = new LicenseClient();

// Validar licencia
const status: LicenseStatus = await licenseClient.validateLicense();

if (!status.valid) {
  throw new Error(`License invalid: ${status.reason}`);
}

// Conectar WebSocket para updates en tiempo real
licenseClient.connectWebSocket();

licenseClient.on('license:deactivated', (data) => {
  console.error('License deactivated:', data);
  process.exit(1);
});

licenseClient.on('license:updated', (data) => {
  console.log('License updated:', data);
});

// Enviar heartbeat
const metrics: HeartbeatMetrics = {
  employeeCount: 150,
  clientCount: 5,
  version: '1.0.0'
};
await licenseClient.sendHeartbeat(metrics);

// Verificar límites
const { allowed, reason } = await licenseClient.checkLimits(5);
if (!allowed) {
  throw new Error(reason);
}

// Verificar feature
const hasAdvancedReports = await licenseClient.hasFeature('advanced_reports');

Configuración

LICENSE_SERVER_URL=https://license.ejemplo.com
INSTALLATION_ID=your-installation-id

Ejemplos Completos

Event Publisher (Backend Realtime)

import { IEventBus, EventBusFactory } from '@empleado-juan/commons-backend';
import { EVENT_TOPICS, type EventMessage } from '@empleado-juan/commons';

class EventPublisher {
  private eventBus: IEventBus;

  constructor() {
    this.eventBus = EventBusFactory.createPublisher();
  }

  async connect() {
    await this.eventBus.connect();
  }

  async publishEmployeeCreated(employee: any) {
    await this.eventBus.publish(EVENT_TOPICS.EMPLOYEE_CREATED, {
      key: employee.id,
      value: {
        id: employee.id,
        firstName: employee.firstName,
        lastName: employee.lastName,
        email: employee.email,
        isActive: employee.isActive
      },
      headers: {
        'event-type': 'employee.created',
        'timestamp': new Date().toISOString()
      }
    });
  }
}

export const eventPublisher = new EventPublisher();

Event Consumer (Backend Analytics)

import { IEventConsumer, EventBusFactory } from '@empleado-juan/commons-backend';
import { EVENT_TOPICS } from '@empleado-juan/commons';

class EventConsumer {
  private eventBus: IEventConsumer;

  constructor() {
    this.eventBus = EventBusFactory.createConsumer();
  }

  async connect() {
    await this.eventBus.connect();
  }

  async subscribe() {
    await this.eventBus.subscribe([
      EVENT_TOPICS.EMPLOYEE_CREATED,
      EVENT_TOPICS.EMPLOYEE_UPDATED,
      EVENT_TOPICS.EMPLOYEE_DELETED
    ]);
  }

  async start() {
    await this.eventBus.start(async (topic, data) => {
      switch (topic) {
        case EVENT_TOPICS.EMPLOYEE_CREATED:
          await this.handleEmployeeCreated(data);
          break;
        case EVENT_TOPICS.EMPLOYEE_UPDATED:
          await this.handleEmployeeUpdated(data);
          break;
        case EVENT_TOPICS.EMPLOYEE_DELETED:
          await this.handleEmployeeDeleted(data);
          break;
      }
    });
  }

  private async handleEmployeeCreated(data: any) {
    // Guardar en DB analytics
    console.log('Employee created:', data);
  }
}

export const eventConsumer = new EventConsumer();

License Validation Middleware

import { Request, Response, NextFunction } from 'express';
import { LicenseClient } from '@empleado-juan/commons-backend';

const licenseClient = new LicenseClient();

export async function requireValidLicense(
  req: Request,
  res: Response,
  next: NextFunction
) {
  try {
    const status = await licenseClient.checkStatus();

    if (!status.valid) {
      return res.status(403).json({
        error: 'License invalid',
        reason: status.reason
      });
    }

    if (status.warning) {
      res.setHeader('X-License-Warning', status.warning);
    }

    next();
  } catch (error) {
    next(error);
  }
}

Arquitectura

Event Bus - Adapter Pattern

EventBusFactory
    ↓
    ├─→ RabbitMQPublisher (implements IEventBus)
    ├─→ KafkaPublisher (implements IEventBus)
    └─→ [Future: SQSPublisher, RedisPublisher, etc.]

Ventajas:

  • ✅ Cambio de broker sin modificar código de negocio
  • ✅ Testing fácil con mocks
  • ✅ Agregar nuevos brokers sin breaking changes

License Client - Singleton con Cache

LicenseClient
    ├─→ HTTP: Validate license
    ├─→ Cache: 5 min TTL
    ├─→ WebSocket: Real-time updates
    └─→ Grace Period: 24-48h offline tolerance

Características:

  • ✅ Cache para evitar validaciones constantes
  • ✅ Grace period si servidor de licencias está down
  • ✅ WebSocket para notificaciones instantáneas
  • ✅ EventEmitter para integración con el sistema

Features por Broker

RabbitMQ

  • ✅ Confirm mode para reliability
  • ✅ Dead Letter Queue (DLQ)
  • ✅ Retry automático (3 intentos)
  • ✅ Prefetch para backpressure
  • ✅ Topic exchange para routing

Kafka

  • ✅ Idempotent producer
  • ✅ Consumer groups para scaling
  • ✅ Partitioning por key
  • ✅ Offset management automático
  • ✅ Rebalancing support

Desarrollo

Setup

git clone <repo>
cd commons-backend
npm install

Build

npm run build

Publish

# Incrementar versión
npm version patch|minor|major

# Compilar
npm run build

# Publicar
npm publish --access public

Dependencias

  • @empleado-juan/commons: ^1.0.0
  • axios: ^1.6.2
  • socket.io-client: ^4.7.2
  • amqplib: ^0.10.3
  • kafkajs: ^2.2.4

License

MIT © Empleado Juan Team