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

@whsflow/sdk

v1.0.0

Published

SDK TypeScript para conectar aplicaciones con backend-clubhx

Readme

ClubHX SDK

SDK TypeScript standalone para conectar aplicaciones frontend/Node.js con backend-clubhx. Diseñado para ser copiado y pegado en cualquier proyecto sin dependencias externas.

Características

  • Type-safe: TypeScript completo con tipos bien definidos
  • Autenticación dual: Soporta service-to-service y Bearer tokens
  • Multi-tenancy: Soporte para X-Shop-Domain header
  • Sin dependencias: Usa solo APIs nativas de Node.js 18+
  • Manejo de errores: Versiones safe() que no lanzan excepciones
  • Modular: Recursos organizados por entidad

Instalación

Copia la carpeta src/ completa a tu proyecto:

cp -r clubhx-sdk/src ./src/lib/clubhx-sdk

O simplemente copia los archivos que necesites según tu caso de uso.

Uso Básico

Autenticación Service-to-Service

Para aplicaciones como whsflow que usan X-Club-Service-Token:

import { ClubClient } from './src/lib/clubhx-sdk/client';

const client = new ClubClient({
  baseUrl: process.env.CLUB_BACKEND_URL || 'http://localhost:3005',
  auth: {
    type: 'service',
    serviceToken: process.env.INTEGRATIONS_SERVICE_TOKEN || '',
  },
  shopDomain: 'mi-tienda.myshopify.com', // Opcional
});

// Listar customers
const customers = await client.customers.list({ page: 1, limit: 20 });

// Usar versión safe (no lanza errores)
const result = await client.customers.listSafe({ page: 1, limit: 20 });
if (result.ok) {
  console.log(result.items);
} else {
  console.error(result.error);
}

Autenticación Bearer Token

Para storefronts que usan tokens JWT de usuario:

import { ClubClient } from './src/lib/clubhx-sdk/client';

const client = new ClubClient({
  baseUrl: process.env.CLUB_BACKEND_URL || 'http://localhost:3005',
  auth: {
    type: 'bearer',
    token: userAccessToken, // Token JWT del usuario
  },
  shopDomain: 'mi-tienda.myshopify.com', // Opcional
});

// Listar events
const events = await client.events.list({ status: 'active' });

Recursos Disponibles

Products (/api/v1/product)

// Listar products
const products = await client.products.list({ limit: 20, offset: 0, search: 'term' });

// Obtener product por ID
const product = await client.products.get('product-uuid');

// Crear product (con imagen opcional)
const newProduct = await client.products.create({
  name: 'Producto Ejemplo',
  code: 'PROD-001',
  price: '15000',
  image: fileObject, // File object para multipart/form-data
});

// Actualizar product completo
const updated = await client.products.update('product-uuid', { name: 'Nuevo Nombre' });

// Actualizar product parcialmente
const patched = await client.products.patch('product-uuid', { price: '20000' });

// Eliminar product
await client.products.delete('product-uuid');

Addresses (/api/v1/addresses)

// Listar addresses
const addresses = await client.addresses.list();

// Crear address
const address = await client.addresses.create({
  customerId: 'customer-uuid',
  street: 'Calle Principal',
  number: '123',
  commune: 'Santiago',
  region: 'Región Metropolitana',
  isDefault: true,
});

// Actualizar address
const updated = await client.addresses.update('address-uuid', { number: '456' });

// Eliminar address
await client.addresses.delete('address-uuid');

// Establecer address como predeterminado
const defaultAddr = await client.addresses.setDefault('address-uuid');

Auth (/api/v1/auth)

// Login
const authResponse = await client.auth.login({
  identification: '12345678-9',
  password: 'password123',
});

// Registro
const registerResponse = await client.auth.register({
  identification: '12345678-9',
  password: 'password123',
  email: '[email protected]',
  name: 'Usuario Ejemplo',
});

Catalog (/api/v1/catalog)

// Listar categories
const categories = await client.catalog.listCategories();

// Crear category
const category = await client.catalog.createCategory({
  name: 'Electrónica',
  description: 'Productos electrónicos',
  order: 1,
});

// Actualizar category
const updated = await client.catalog.updateCategory('category-uuid', { name: 'Electronics' });

// Eliminar category
await client.catalog.deleteCategory('category-uuid');

// Listar brands
const brands = await client.catalog.listBrands();

Wishlist (/api/v1/wishlist)

// Obtener analytics de wishlist
const analytics = await client.wishlist.getAnalytics();

Loyalty Points (/api/v1/loyalty)

// Obtener puntos de lealtad
const points = await client.loyaltyPoints.getPoints();

// Obtener puntos próximos a expirar
const expiring = await client.loyaltyPoints.getPointsExpiring();

// Obtener historial de puntos ganados
const earned = await client.loyaltyPoints.getPointsEarned({ limit: 20, offset: 0 });

Delivery (/api/v1/delivery)

// Generar labels de envío
const labels = await client.delivery.generateLabels({
  orderIds: ['order-uuid-1', 'order-uuid-2'],
});

// Obtener tracking
const tracking = await client.delivery.getTracking('order-uuid');

// Actualizar estados masivamente
const updates = await client.delivery.bulkUpdateStatuses({
  orderIds: ['order-uuid-1', 'order-uuid-2'],
});

Dashboard (/api/v1/dashboard)

// Obtener métricas
const metrics = await client.dashboard.getMetrics({ period: 'month' });

Customers (/api/v1/clients)

// Listar customers
const customers = await client.customers.list({ page: 1, limit: 20 });
const result = await client.customers.listSafe({ page: 1, limit: 20 });

Orders (/api/v1/order)

// Listar orders
const orders = await client.orders.list({ page: 1, limit: 10 });
const result = await client.orders.listSafe({ page: 1, limit: 10 });

Loyalty Rewards (/api/v1/loyalty-rewards)

// Listar rewards
const rewards = await client.loyalty.list({ page: 1, limit: 10, category: 'Descuentos' });

// Crear reward
const newReward = await client.loyalty.create({
  name: 'Descuento 10%',
  category: 'Descuentos',
  points_required: 100,
  stock_quantity: 50,
  is_featured: true,
});

// Obtener estadísticas
const stats = await client.loyalty.getStats();

Vendors (/api/v1/vendors)

// Listar vendors
const vendors = await client.vendors.list();

// Crear vendor
const vendor = await client.vendors.create({
  name: 'Juan Pérez',
  email: '[email protected]',
  status: 'active',
});

// Establecer goal
const goal = await client.vendors.setGoal({
  vendorId: 'vendor-uuid',
  period: '2026-01',
  salesTarget: 5000000,
});

Visits (/api/v1/visits)

// Listar visits
const visits = await client.visits.list({
  from: '2026-01-01',
  to: '2026-01-31',
  salesPersonId: 'vendor-uuid',
});

// Crear visit
const visit = await client.visits.create({
  customerId: 'customer-uuid',
  date: '2026-01-15',
  time: '14:00',
  duration: 60,
  status: 'scheduled',
});

// Actualizar visit
const updated = await client.visits.update('visit-uuid', { status: 'completed' });

// Eliminar visit
await client.visits.delete('visit-uuid');

Events (/api/v1/events)

// Listar events
const events = await client.events.list({
  page: 1,
  limit: 10,
  status: 'active',
  isPublic: true,
});

// Crear event
const event = await client.events.create({
  title: 'Evento de Lanzamiento',
  description: 'Descripción del evento',
  start_date: '2026-02-01T10:00:00Z',
  end_date: '2026-02-01T18:00:00Z',
  currency: 'CLP',
  max_capacity: 100,
  is_public: true,
});

// Obtener estadísticas
const stats = await client.events.getStats();

Credit Requests (/api/v1/credit-requests)

// Listar credit requests
const requests = await client.creditRequests.list({
  page: 1,
  limit: 20,
  status: 'pending',
});

// Crear request
const request = await client.creditRequests.create({
  customerId: 'customer-uuid',
  customerName: 'Cliente Ejemplo',
  requestedBy: 'user-uuid',
  requestedLimit: 1000000,
});

// Aprobar request
const approved = await client.creditRequests.approve({
  id: 'request-uuid',
  notes: 'Aprobado por admin',
});

// Obtener estadísticas
const stats = await client.creditRequests.getStats();

Shipping Types (/api/v1/shippingtype)

// Listar shipping types
const types = await client.shipping.list();
const result = await client.shipping.listSafe();

Payment Methods (/api/v1/paymentmethod)

// Listar payment methods
const methods = await client.payment.list();
const result = await client.payment.listSafe();

Integrations (/integrations/*)

// Onboard de tienda Shopify
const onboard = await client.integrations.onboardShopifyShop({
  shop_domain: 'mi-tienda.myshopify.com',
  access_token: 'shpat_...',
  scopes: 'read_products,write_products',
});

// Obtener conexión activa
const connection = await client.integrations.getActiveShopConnection('mi-tienda.myshopify.com');

// Listar conexiones
const connections = await client.integrations.listShopConnections('mi-tienda.myshopify.com');

// Forward webhook
await client.integrations.forwardShopifyWebhookToClub({
  shop_domain: 'mi-tienda.myshopify.com',
  topic: 'orders/create',
  webhook_id: 'webhook-id',
  payload: { /* webhook data */ },
});

Configuración

Variables de Entorno

# URL del backend
CLUB_BACKEND_URL=http://localhost:3005

# Para service-to-service auth
INTEGRATIONS_SERVICE_TOKEN=your-service-token

# Para Bearer auth (obtenido del login)
USER_ACCESS_TOKEN=eyJhbGc...

Logger Personalizado

Puedes proporcionar un logger personalizado:

const client = new ClubClient({
  baseUrl: 'http://localhost:3005',
  auth: { type: 'service', serviceToken: 'token' },
  logger: {
    error: (msg, meta) => console.error(`[ERROR] ${msg}`, meta),
    warn: (msg, meta) => console.warn(`[WARN] ${msg}`, meta),
    info: (msg, meta) => console.info(`[INFO] ${msg}`, meta),
    debug: (msg, meta) => console.debug(`[DEBUG] ${msg}`, meta),
  },
});

Migración desde whsflow

Si estás migrando desde whsflow/app/lib/club.server.ts:

Antes:

import { listCustomersSafe } from '../lib/club.server';

const result = await listCustomersSafe({
  shop_domain: 'mi-tienda.myshopify.com',
  authorization: undefined,
  page: 1,
  limit: 20,
});

Después:

import { ClubClient } from './lib/clubhx-sdk/client';

const client = new ClubClient({
  baseUrl: process.env.CLUB_BACKEND_URL,
  auth: { type: 'service', serviceToken: process.env.INTEGRATIONS_SERVICE_TOKEN },
  shopDomain: 'mi-tienda.myshopify.com',
});

const result = await client.customers.listSafe({ page: 1, limit: 20 });

Ventajas:

  1. Centralización: Toda la lógica de conexión está en un solo lugar
  2. Type-safety: TypeScript completo con autocompletado
  3. Reutilizable: Fácil copiar a otros proyectos
  4. Mantenible: Agregar nuevos recursos no requiere modificar código existente

Estructura del SDK

src/
├── client.ts              # Clase principal ClubClient
├── config.ts              # Tipos de configuración
├── auth/
│   ├── service-auth.ts    # Autenticación service-to-service
│   └── bearer-auth.ts     # Autenticación Bearer token
├── resources/
│   ├── customers.ts       # Resource de customers
│   ├── orders.ts          # Resource de orders
│   ├── loyalty.ts         # Resource de loyalty
│   └── ...                # Otros resources
├── types/
│   ├── common.ts          # Tipos compartidos
│   └── ...                # Tipos por recurso
└── utils/
    ├── fetch.ts           # Helpers para fetch
    └── url.ts             # Normalización de URLs

Requisitos

  • Node.js 18+ (para fetch nativo)
  • TypeScript 4.5+

Ejemplos

Ver examples/ para ejemplos completos de uso:

  • examples/service-auth.example.ts - Uso con service-to-service auth
  • examples/bearer-auth.example.ts - Uso con Bearer token auth

Licencia

Este SDK es parte del proyecto ClubHX y está sujeto a la misma licencia.