Node.js

El paquete honk-me es un cliente en TypeScript sin dependencias en tiempo de ejecución, para Node 18 o posterior, Bun y Deno. Úsalo en código de backend, funciones y scripts.

Paquete
honk-me Publicado
Código fuente
github.com/honk-me/honk-node · Licencia MIT
Requisitos
Node 18 o posterior (fetch global), Bun o Deno.

Instalación

Después, crea un proyecto y una clave de ingesta en la app web de Honk.

Terminal
npm install honk-me

Quien tenga una clave de ingesta (honk_…) puede enviar mensajes a su proyecto. Guárdala en servidores, tareas y secretos de CI, nunca en un navegador ni en una app móvil o de escritorio.

Enviar un evento

Honk.fromEnv() lee HONK_URL, HONK_KEY y los valores por defecto opcionales HONK_SOURCE, HONK_ENVIRONMENT y HONK_CHANNEL. En CommonJS también funciona require('honk-me').

import { Honk } from 'honk-me';

const honk = new Honk({ url: process.env.HONK_URL, key: process.env.HONK_KEY });
await honk.beep('Backup finished', 'nightly pg_dump took 42 s');

Receta: una solicitud de cliente en tu teléfono

Un grupo por solicitud y una clave de idempotencia estable: dos clientes nunca se juntan en una misma notificación, y un webhook reintentado nunca suena dos veces. No se hace await de la promesa, así que una red lenta nunca retrasa la respuesta al cliente.

import { Honk } from 'honk-me';

const honk = Honk.fromEnv(); // create once, reuse (keep-alive)

export async function onCustomerRequest(req) {
  // ...save the request first, then notify without blocking the response:
  honk
    .send(
      {
        title: `New request: ${req.subject}`.slice(0, 160),
        message: `${req.name} (${req.company}) asked: ${req.body}`.slice(0, 2000),
        priority: 'high', // push right away
        category: 'customers',
        channel: 'requests',
        groupKey: `requests/${req.id}`, // one group per request
        url: `https://shop.example.com/admin/requests/${req.id}`, // https only, shown as "Open link"
        metadata: { request_id: String(req.id) },
      },
      { idempotencyKey: `request-${req.id}` }, // same request, same key: never a duplicate
    )
    .catch((err) => console.warn('honk:', err.message)); // never fail the customer's request
}

La escala Honk e incidentes

Un helper por nivel. problem y recovery requieren una clave de grupo; la recuperación cierra el episodio que abrió el problema.

await honk.loud('Disk 91%', '/var on app-01', { groupKey: 'disk/app-01/var' });
await honk.light('Deploy started', 'v4.2.0 to production', { channel: 'deploys' });
await honk.problem('db/backup', 'Backup failed', 'pg_dump exited with 1');      // a long honk by default
await honk.recovery('db/backup', 'Backup OK', 'pg_dump finished in 41 s');      // a beep by default

Errores

Todos los errores son HonkError, con kind y retryable. Los de validación son fallos de programación; los reintentables pueden volver a tu cola con la misma clave.

import { HonkError, HonkValidationError } from 'honk-me';

try {
  await honk.send(msg, { idempotencyKey: `order-${order.id}-failed` });
} catch (err) {
  if (err instanceof HonkValidationError) logger.error(err.fields);   // a bug: don't retry
  else if (err instanceof HonkError && err.retryable) queue.retryLater(err.idempotencyKey, err.retryAfter);
  else throw err;
}

Bun y Deno

Bun: bun add honk-me y úsalo como en Node. Deno lo importa desde npm:

import { Honk } from 'npm:honk-me'; // Deno
const honk = new Honk({ url: Deno.env.get('HONK_URL')!, key: Deno.env.get('HONK_KEY')! });

Reintentos que nunca envían dos veces

  • Cada envío lleva una Idempotency-Key: la tuya o un UUIDv7 nuevo. La misma clave se reutiliza en cada reintento y, durante 24 horas, Honk responde a una repetición con el id original y duplicate: true, así que una respuesta perdida nunca crea un segundo mensaje.
  • Solo se reintentan los errores de red, los tiempos de espera agotados, 429 y 5xx, con backoff exponencial y jitter completo, y nunca antes del Retry-After del servidor. Las demás respuestas 4xx no se reintentan nunca: hay que corregir la solicitud.
  • Cada intento se corta a los 5 segundos y todo se detiene a los 30. Una espera que superaría ese plazo, como una cuota diaria que se reinicia a medianoche, falla al momento y te dice cuándo reintentar.
  • Los campos se validan antes de enviar, y todos los errores de validación se devuelven juntos.