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

@wearedev-mgrr/sdk

v1.0.0

Published

Official TypeScript SDK for the MGRR (Motor de Generacion de Reportes Regulatorios) API

Readme

@wearedev-mgrr/sdk

Cliente TypeScript oficial para el Motor de Generación de Reportes Regulatorios (MGRR) de WeAre. Permite listar reportes, ejecutarlos, consultar su estado y descargar los archivos generados desde Node.js o browser.

Estado: 0.2.0 — superficie admin completa + exchange de API key por JWT. Ver CHANGELOG.md.


Install

npm install @wearedev-mgrr/sdk
# o
pnpm add @wearedev-mgrr/sdk
# o
yarn add @wearedev-mgrr/sdk

Requiere Node >= 20 (para fetch y AbortSignal globales) o un browser moderno (ES2022).


Quickstart

Node

import { MgrrClient } from '@wearedev-mgrr/sdk';

const mgrr = new MgrrClient({
  apiKey: process.env.MGRR_API_KEY!,
  baseUrl: 'https://api.mgrr.weare.co',
});

const { data: reports } = await mgrr.reports.list({ limit: 20 });
const exec = await mgrr.executions.run('UIAF_CAMBISTAS', {
  params: { year: 2026, month: 3 },
});

await exec.waitUntilDone({ timeoutMs: 600_000 });
const dl = await exec.download();
await dl.pipeToFile('./out/uiaf_cambistas_2026_03.txt');

Browser

import { MgrrClient } from '@wearedev-mgrr/sdk';

const mgrr = new MgrrClient({ apiKey: userKey, baseUrl: '/api' });
const exec = await mgrr.executions.run('DIAN_1100', { params: { year: 2026 } });
await exec.waitUntilDone();
const blob = await (await exec.download()).toBlob();

Matriz de compatibilidad

| Entorno | Version minima | Notas | |-----------------|----------------|---------------------------------------------------| | Node.js | >= 20 | fetch, AbortSignal nativos. | | Browser (ESM) | ES2022 | Chrome 94+, Firefox 93+, Safari 15.4+, Edge 94+. | | TypeScript | >= 5.0 | Tipos incluidos (.d.ts). |

| SDK version | API / OpenAPI | Estado | |-------------|---------------|--------| | 0.x | v0 (pre-GA) | beta | | 1.x | v1 | GA |


Configuración

new MgrrClient({
  apiKey: 'mgrr_acme_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // requerido
  baseUrl: 'https://api.mgrr.weare.co',                  // default: http://localhost:8080
  timeoutMs: 30_000,                                     // HTTP request timeout (default 30s)
  userAgent: 'my-app/1.0',                               // opcional
  fetch: customFetch,                                    // override (tests, proxies)
  logger: (level, msg, data) => console.log(level, msg, data),
  defaultPoll: { pollIntervalMs: 1000, maxIntervalMs: 30_000 },
});

| Opción | Tipo | Default | |---------------|--------------------------------------|----------------------------| | apiKey | string (requerido) | — | | baseUrl | string | http://localhost:8080 | | timeoutMs | number | 30000 | | userAgent | string | mgrr-sdk/<version> | | fetch | typeof fetch | globalThis.fetch | | logger | (level, msg, data?) => void | undefined | | defaultPoll | PollOptions | backoff 1s → 30s, 30 min |


Uso

Listar reportes

const page = await mgrr.reports.list({ limit: 50, offset: 0 });
page.data.forEach((r) => console.log(r.code, r.name));

const detail = await mgrr.reports.get('UIAF_CAMBISTAS');
console.log(detail.parameters);

Ejecutar un reporte

const exec = await mgrr.executions.run('UIAF_CAMBISTAS', {
  params: { year: 2026, month: 3 },
  idempotencyKey: 'uiaf-2026-03-run-1', // opcional
});
console.log(exec.id, exec.status); // 'QUEUED'

Esperar hasta finalizar (waitUntilDone)

Azúcar sobre el polling. Resuelve con DoneEvent o rechaza con un error tipado.

const done = await exec.waitUntilDone({
  pollIntervalMs: 1000,
  maxIntervalMs: 30_000,
  timeoutMs: 600_000,
});
console.log(done.fileUrl, done.rowCount, done.durationSeconds);

Eventos (EventEmitter)

Para UIs reactivas o logs en vivo.

exec.on('progress', (p) => console.log(`[${p.status}] ${p.progressPercentage}%`));
exec.on('done', (d) => console.log('Listo:', d.fileUrl));
exec.on('error', (e) => console.error(e.code, e.message));

await exec.waitUntilDone();

Descargar el archivo

const dl = await exec.download();

// Node
await dl.pipeToFile('./out/report.txt');

// Isomórfico
const bytes: Uint8Array = await dl.toBuffer();

// Browser
const blob: Blob = await dl.toBlob();
const url = URL.createObjectURL(blob);

Cancelación con AbortSignal

const controller = new AbortController();
setTimeout(() => controller.abort(), 60_000);

const exec = await mgrr.executions.run('DIAN_1099', {
  params: { year: 2026 },
  signal: controller.signal,
});

await exec.waitUntilDone({ signal: controller.signal });

Execution.cancel() aborta el polling local (no cancela la ejecución en el servidor en v1).


Errores

Todos los errores heredan de MgrrError y exponen code, statusCode, requestId y details.

| Clase | HTTP | Cuando se lanza | Campos extras | |--------------------------------|---------|---------------------------------------------------------------|-------------------------------| | MgrrNetworkError | — | Fetch fail, DNS, connection reset. | — | | MgrrTimeoutError | — | HTTP request o polling excedio timeoutMs. | — | | MgrrAbortError | — | AbortSignal disparado por el consumidor. | — | | MgrrValidationError | 400 | Parametros invalidos (error.code = VALIDATION_ERROR). | details: [{field, message}] | | MgrrAuthError | 401/403 | INVALID_API_KEY, EXPIRED, FORBIDDEN. | — | | MgrrNotFoundError | 404 | REPORT_NOT_FOUND, FILE_NOT_FOUND, execution no existe. | — | | MgrrConflictError | 409 | REPORT_INACTIVE, NOT_READY (archivo aun no generado). | — | | MgrrRateLimitError | 429 | RATE_LIMITED. Respeta header Retry-After. | details.retryAfterMs | | MgrrServerError | 5xx | Error del servidor MGRR. | — | | MgrrExecutionFailedError | — | waitUntilDone() detecta status = FAILED. | details.errorMessage | | MgrrExecutionCancelledError | — | waitUntilDone() detecta status = CANCELLED. | — | | MgrrUsageError | — | Mal uso del SDK (ej. apiKey faltante, pipeToFile en browser). | — | | MgrrError (base) | * | Cualquier otro 400 o respuesta con shape desconocido. | details: <body crudo> |

Ejemplo try/catch tipado

import {
  MgrrClient,
  MgrrValidationError,
  MgrrAuthError,
  MgrrRateLimitError,
  MgrrExecutionFailedError,
  MgrrTimeoutError,
} from '@wearedev-mgrr/sdk';

try {
  const exec = await mgrr.executions.run('UIAF_CAMBISTAS', {
    params: { year: 2026, month: 3 },
  });
  await exec.waitUntilDone({ timeoutMs: 300_000 });
  const dl = await exec.download();
  await dl.pipeToFile('./out.txt');
} catch (e) {
  if (e instanceof MgrrValidationError) {
    console.error('Parámetros inválidos:', e.details);
  } else if (e instanceof MgrrAuthError) {
    console.error('API key inválida o sin permisos');
  } else if (e instanceof MgrrRateLimitError) {
    console.error('Rate limited, reintentar en', e.details);
  } else if (e instanceof MgrrExecutionFailedError) {
    console.error('Ejecución falló:', e.message, 'requestId:', e.requestId);
  } else if (e instanceof MgrrTimeoutError) {
    console.error('Timeout esperando al reporte');
  } else {
    throw e;
  }
}

Todo error incluye error.requestId (header X-Request-Id) para correlacionar con los logs del API.

Mapeo por status HTTP (0.2.0)

Desde 0.2.0 el mapeo es por status code, no por error.code:

| Status | Clase lanzada | Alias corto | |--------|---------------------------|---------------------| | 400 | MgrrValidationError | ValidationError | | 401 | MgrrAuthError | — | | 403 | MgrrForbiddenError | ForbiddenError | | 404 | MgrrNotFoundError | NotFoundError | | 409 | MgrrConflictError | ConflictError | | 429 | MgrrRateLimitError | RateLimitedError | | 5xx | MgrrApiError | ApiError |

MgrrForbiddenError extends MgrrAuthError, asi que instanceof MgrrAuthError sigue capturando 401 y 403 — back-compat con codigo pre-0.2.0. Para distinguir 403 puro usa instanceof MgrrForbiddenError.


Auth: exchange de API key por JWT

client.auth.exchangeApiKey() intercambia la API key configurada (o una pasada como argumento) por un JWT Bearer de 300 s. Pensado para apps browser (ej. sdk-visual) que no deben persistir la API key cruda en localStorage — el token corto se guarda en memoria y se refresca al vencer.

El MgrrClient soporta dos modos de auth mutuamente excluyentes:

  • API key (default): emite X-API-Key: <apiKey> en cada request.
  • Bearer JWT: tras setAccessToken(jwt), emite Authorization: Bearer <jwt> y deja de enviar X-API-Key. Es el modo requerido para client.admin.*, ya que el middleware de admin rechaza la API key.

Quickstart recomendado (browser / sdk-visual)

const mgrr = new MgrrClient({ apiKey, baseUrl: '/api' });

// One-shot: intercambia la key, instala el Bearer internamente.
const { access_token, expires_in } = await mgrr.authenticateWithApiKey();
// a partir de aqui client.admin.* funciona:
const { data: reports } = await mgrr.admin.reports.list();

// Al vencer (expires_in = 300s) refresca con la misma llamada — el
// exchange usa siempre X-API-Key, incluso si ya habia un JWT instalado.
await mgrr.authenticateWithApiKey();

// Logout: vuelve al modo X-API-Key (util para catalogo publico).
mgrr.setAccessToken(null);

API de auth

// Intercambio explicito — retorna el ExchangeResponse sin instalar nada.
const res = await mgrr.auth.exchangeApiKey();

// Instala un JWT obtenido por otro medio (ej. login UI).
mgrr.setAccessToken(res.access_token);

// Limpia el Bearer y vuelve a la API key original.
mgrr.setAccessToken(null);

Cuando usar cada modo

| Modo | Setup | Cuando | |----------------------|---------------------------------------------|-------------------------------------------------------------------------| | API key directo | Default — ningun setup | Node / scripts CI / servicios backend. La key vive en env vars. | | Bearer via exchange | authenticateWithApiKey() | Browser SPA. Convierte la key a un token corto, habilita admin.*. | | Bearer JWT externo | setAccessToken(jwtFromLogin) | UI admin con login password + TOTP que emite su propio JWT. |


Admin namespace

Agrupa todas las operaciones de autoria + administracion. Require API key con role ADMIN (o el rol correspondiente server-side). Todas las escrituras son tenant-scoped automaticamente.

// Reportes — CRUD + clone
const { data: reports } = await mgrr.admin.reports.list({ limit: 20 });
const full = await mgrr.admin.reports.get(42);                // ReportFull hidratado
const created = await mgrr.admin.reports.create({ code: 'UIAF_NEW', name: '...', output_format: 'FIXED_WIDTH' });
const cloned = await mgrr.admin.reports.clone(42, { code: 'UIAF_COPY' });

// Parametros
await mgrr.admin.parameters.create({ report_id: 42, code: 'year', data_type: 'INTEGER', required: true });
await mgrr.admin.parameters.update(parameterId, { default_value: '2026' });
await mgrr.admin.parameters.delete(parameterId);

// Secciones — CRUD + reorder
await mgrr.admin.sections.list(42);
await mgrr.admin.sections.reorder(42, [sec1Id, sec2Id, sec3Id]);

// Campos — CRUD + carga batch (util en el wizard)
await mgrr.admin.fields.batchAdd(sectionId, [
  { code: 'nit', data_type: 'STRING', position: 1, length: 11 },
  { code: 'razon_social', data_type: 'STRING', position: 2, length: 100 },
]);

// Validaciones — CRUD + batch
await mgrr.admin.validations.batchCreate(fieldId, [
  { rule_type: 'REQUIRED', rule_config: {} },
  { rule_type: 'LENGTH_MAX', rule_config: { max: 100 } },
]);

// Post-process (put idempotente)
await mgrr.admin.postProcess.set(42, { compression_type: 'GZIP', encryption_type: 'NONE' });
const pp = await mgrr.admin.postProcess.get(42); // null si no hay

// Credenciales — respuestas SIEMPRE enmascaradas
const { data: creds } = await mgrr.admin.credentials.list({ type: 'DB_POSTGRES' });
const testResult = await mgrr.admin.credentials.test(credId);  // { ok: true/false, error? }

// Usuarios (tenant-scoped)
const user = await mgrr.admin.users.create({ email: '[email protected]', full_name: '...', role: 'AUTHOR' });
const { temporary_password } = await mgrr.admin.users.resetPassword(user.id);
await mgrr.admin.users.resetTotp(user.id);

// API Keys — `key` crudo mostrado UNA sola vez
const issued = await mgrr.admin.apiKeys.issue({ name: 'ci-prod', role: 'EXECUTOR' });
console.log(issued.key);    // guardar ya, no se recupera despues
const rotated = await mgrr.admin.apiKeys.rotate(issued.id);   // emite nueva, revoca vieja
await mgrr.admin.apiKeys.audit(issued.id, { limit: 100 });

// Tools — helpers de autoria
await mgrr.admin.tools.testSource({ source_type: 'DB_POSTGRES', credential_id, query: 'SELECT 1' });
await mgrr.admin.tools.testQuery({ credential_id, query: 'SELECT * FROM ventas LIMIT 10' });
await mgrr.admin.tools.previewJsonata({ expression: 'total * 1.19', sample_row: { total: 100 } });
await mgrr.admin.tools.previewFile({ report_id: 42, params: { year: 2026 }, sample_size: 5 });

Streaming downloads

Para archivos grandes (>10 MB) usa downloadExecutionFile en lugar de exec.download().toBuffer() / toBlob() — evita cargar el archivo completo en heap.

import { downloadExecutionFile } from '@wearedev-mgrr/sdk';
import { pipeline } from 'node:stream/promises';
import { createWriteStream } from 'node:fs';

// Node — Readable pipeable a fs
const { stream, filename, contentLength } = await downloadExecutionFile(
  mgrr,
  executionId,
  { asNodeStream: true },
);
await pipeline(stream, createWriteStream(`./out/${filename ?? 'report.bin'}`));

// Browser — web ReadableStream (default)
const { stream } = await downloadExecutionFile(mgrr, executionId);
const res = new Response(stream);
const blob = await res.blob();   // o pipe a otro lado

Desarrollo

Regenerar tipos desde OpenAPI

npm run dev --workspace=packages/api                     # 1. Levantar API
curl -s http://localhost:8080/docs/json > packages/sdk/openapi.json
npm run gen:types --workspace=packages/sdk

npm run check:types-fresh falla si src/schema.d.ts quedo desactualizado respecto a openapi.json.

Build

npm run build       # dual ESM + CJS + .d.ts via tsup
npm run typecheck
npm test

Ejemplos


Licencia

Propietario — WeAre S.A.S. Uso interno.