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

@araitek/smartdocjs

v0.1.2

Published

SDK de Node.js para SmartDoc — facturación electrónica en Paraguay (DNIT/SIFEN)

Readme

@araitek/smartdocjs

SDK de Node.js y TypeScript para SmartDoc, el sistema de facturación electrónica para Paraguay. Cubre la API pública v2 y la recepción de sus webhooks.

Documentación completa: https://www.smartdoc.com.py/docs/node/

Instalación

npm install @araitek/smartdocjs

Requiere Node.js 18 o superior. No tiene dependencias de terceros.

Funciona igual con ESM y con CommonJS, y trae sus propios tipos:

import { Client, Item, Recipient } from '@araitek/smartdocjs';       // ESM / TypeScript
const { Client, Item, Recipient } = require('@araitek/smartdocjs');  // CommonJS

Emitir un documento

import { Client, Item, Recipient } from '@araitek/smartdocjs';

const sd = new Client();          // toma la API Key del entorno

let factura = await sd.invoices.create({
  recipient: Recipient.entity({
    ruc: '80012345-1',
    socialName: 'Cliente Ejemplo S.A.',
    email: '[email protected]',
  }),
  items: [
    new Item('Consultoría de octubre', { quantity: 1, unitAmount: 1_100_000 }),
  ],
});

factura = await sd.invoices.waitUntilFinal(factura.id);
console.log(factura.status, factura.cdc);

El SDK completa los datos del emisor, la clave de idempotencia y el desglose de IVA. En Paraguay el IVA va incluido en el precio: sobre ese ejemplo emite 1.000.000 de base gravada y 100.000 de IVA.

Están los seis tipos de documento electrónico, cada uno con sus acciones:

| Recurso | Documento | |---|---| | sd.invoices | Factura | | sd.receipts | Recibo | | sd.creditNotes | Nota de crédito | | sd.debitNotes | Nota de débito | | sd.remissionNotes | Nota de remisión | | sd.autoInvoices | Autofactura |

Configuración

La API Key sale de SMARTDOC_API_KEY. La URL apunta a la instancia de producción; solo hace falta SMARTDOC_BASE_URL con una instancia propia.

export SMARTDOC_API_KEY="pk_..."

Con más de un establecimiento o punto de expedición hay que decir cuál, porque el SDK no elige por su cuenta:

const sd = new Client({ establishment: '001', dispatchPoint: '001' });

o por llamada, si emitís desde varias sucursales:

await sd.invoices.create({
  recipient,
  items,
  establishment: '002',
  dispatchPoint: '001',
});

Idempotencia

Cada create() va con una clave nueva que genera el SDK, así que reintentar nunca emite dos veces. Si preferís que la clave venga de tu sistema:

const factura = await sd.invoices.create({
  recipient,
  items,
  idempotencyKey: `venta-${venta.id}`,
});

Repetir la llamada con la misma clave devuelve el documento ya creado.

Errores

import { RateLimitError, ServerError, ValidationError } from '@araitek/smartdocjs';

try {
  await sd.invoices.create({ recipient, items });
} catch (error) {
  if (error instanceof ValidationError) {
    // datos mal armados; no sirve reintentar
  } else if (error instanceof RateLimitError || error instanceof ServerError) {
    // transitorio; el SDK ya reintentó
  } else {
    throw error;
  }
}

Todos heredan de SmartDocError. Las validaciones que el SDK puede hacer sin red —largos, catálogos cerrados, campos condicionales— fallan antes de salir, con el mensaje de qué corregir.

Webhooks

En producción conviene escuchar los eventos en vez de hacer polling.

import express from 'express';
import { Event, Webhooks } from '@araitek/smartdocjs';

const webhooks = new Webhooks({ secret: process.env.SMARTDOC_WEBHOOK_SECRET });

webhooks.on(Event.INVOICE_APPROVED, async (evento) => {
  await marcarAprobada(evento.entityId, evento.cdc);
});

webhooks.onAnyError(async (evento) => {
  await avisar(`${evento.errorCode}: ${evento.errorMessage}`);
});

const app = express();
app.post(
  '/webhooks/smartdoc',
  express.raw({ type: 'application/json' }),   // la firma va sobre el cuerpo crudo
  webhooks.expressHandler(),
);

Hay adaptadores para Express y Fastify, y uno con Request/Response estándar para Hono, Next.js, Deno y Bun. Para desarrollo, webhooks.serve({ port: 3000 }) levanta un servidor mínimo.

El SDK verifica la firma HMAC sobre el cuerpo crudo, descarta las entregas repetidas y rechaza las que llegan fuera de la ventana de tolerancia.

Catálogos y geografía

Los catálogos de SIFEN vienen validados. Un valor inválido falla antes de salir a la red:

import { Iva, SaleType, geo } from '@araitek/smartdocjs';

SaleType.CASH;                 // 'cash'
Iva.TEN;                       // '10_percent'
SaleType.coerce('Contado');    // ValidationError: ¿Quisiste decir "cash"?

geo.findCity('Asunción');      // resuelve departamento, distrito y ciudad

Desarrollo

npm install
npm run build        # ESM + CJS + tipos
npm test             # compila y corre la suite
npm run typecheck
npm run docs:dev     # el sitio de documentación, en local

Los tests de integración corren contra una instancia real y se saltean si no hay credenciales. Copiá .env.example a .env y completalo:

npm run e2e

Conviene apuntarlos a un contribuyente demo: ahí SmartDoc simula la respuesta de la DNIT en vez de enviarla.

En examples/ hay tres programas completos —emitir una factura, recibir webhooks y el ciclo entero incluido el camino de error—. Para correrlos desde el repo, con el paquete construido:

npm link                              # deja @araitek/smartdocjs resoluble
node examples/01-emitir-factura.mjs

Licencia

MIT.