npm package: @hemia/workflow-node-wait-for-webhook
Description: Nodo de workflow de Hemia para esperar un webhook.
Author: Hemia Technologies
Latest version: 0.0.3

# @hemia/workflow-node-wait-for-webhook

Nodo para pausar un workflow hasta recibir un webhook externo. Diseñado para integrarse con `@hemia/workflow-engine` y permitir flujos de aprobación, formularios, pasos manuales, etc.

## Instalación

```bash
npm install @hemia/workflow-node-wait-for-webhook
```

Requiere Node.js >= 18.

## Qué exporta

- `waitForWebhookNode`: instancia lista para registrar en el engine.
- `WaitForWebhookNode`: clase por si prefieres instanciar manualmente.

> Nota: Asegúrate de que el paquete exporte estos símbolos desde su `index.ts`.

## Uso con hemia-workflow-engine

### 1) Registrar el nodo y ejecutar el workflow

```ts
import { WorkflowEngine, NodeRegistry } from "@hemia/workflow-engine";
import { waitForWebhookNode } from "@hemia/workflow-node-wait-for-webhook";
import type { Workflow } from "@hemia/workflow-core";

// Persistencia (implementación propia)
const saveContext = async (_ctx: any, _status?: string) => {};
const loadContext = async (_executionId: string) => undefined;

const registry = new NodeRegistry();
registry.register("wait-for-webhook", waitForWebhookNode);

const workflow: Workflow = {
  id: "order-approval",
  name: "Aprobación de pedido",
  steps: [
    // ... pasos previos
    {
      id: "pause-for-approval",
      type: "wait-for-webhook",
      params: {
        stepId: "pause-for-approval",
        reason: "approval",
        metadata: { entity: "order", id: "123" }
      }
    },
    // ... pasos siguientes que se ejecutarán tras reanudar
  ]
};

// La ejecución se identifica por executionId (guárdalo en tu BD)
const engine = new WorkflowEngine(
  workflow,
  "exec-001",
  registry,
  saveContext,
  loadContext
);

await engine.run(); // Se pausará con estado WAITING en el paso wait-for-webhook
```

### 2) Reanudar tras recibir el webhook

Ejemplo con Express:

```ts
import express from "express";
import { WorkflowEngine, NodeRegistry } from "@hemia/workflow-engine";
import { waitForWebhookNode } from "@hemia/workflow-node-wait-for-webhook";

const app = express();
app.use(express.json());

const registry = new NodeRegistry();
registry.register("wait-for-webhook", waitForWebhookNode);

app.post("/webhooks/order-approved", async (req, res) => {
  const { executionId, stepId, payload } = req.body; // envía estos datos desde el emisor del webhook

  // Tus funciones deben cargar/guardar el contexto por executionId
  const saveContext = async (_ctx: any, _status?: string) => {};
  const loadContext = async (_executionId: string) => ({ /* contexto previo */ payload });

  const engine = new WorkflowEngine(
    /* mismo workflow */, 
    executionId,
    registry,
    saveContext,
    loadContext
  );

  await engine.resume(stepId); // Continúa el flujo a partir del paso indicado
  res.sendStatus(202);
});

app.listen(3000);
```

## Configuración del nodo

Parámetros (`params`) aceptados por `wait-for-webhook`:

- `stepId` (string, requerido): identificador del paso donde se espera.
- `reason` ("form" | "approval" | "manual" | "external", opcional): motivo de la espera.
- `timeout` (number | string, opcional): tiempo máximo de espera. Preferido en milisegundos (number). Si es string, se admite `ms`, `s`, `m`, `h` (p.ej. `"30s"`, `"1h"`). Si no se define, se usa `config.timeouts.approval` (si está disponible en el contexto).
- `formId` (string, opcional): id de formulario asociado.
- `metadata` (object, opcional): datos adicionales relevantes al negocio.

Esquema simplificado:

```json
{
  "type": "object",
  "required": ["stepId"],
  "properties": {
    "stepId": { "type": "string" },
    "reason": { "type": "string", "enum": ["form", "approval", "manual", "external"] },
    "timeout": { "type": ["number", "string"] },
    "formId": { "type": "string" },
    "metadata": { "type": "object" }
  }
}
```

## Comportamiento de ejecución

- El nodo devuelve `success: false` con `error.code = "WAITING"` y el `WorkflowEngine` lo interpreta como pausa.
- Timeout efectivo: si `params.timeout` no está presente, se intenta resolver desde `variables.config.timeouts.approval`. Internamente se calcula `timeoutMs` (milisegundos) si hay valor.
- Estados típicos que guardará el engine mediante `saveContext`:
  - `WAITING`: pausado esperando webhook
  - `RUNNING`: en ejecución al reanudar
  - `FAILED`: error
  - `COMPLETED`: finalizado

Ejemplo de resultado del nodo:

```json
{
  "success": false,
  "error": {
    "code": "WAITING",
    "message": "Esperando webhook para paso 'pause-for-approval'",
    "details": {
      "stepId": "pause-for-approval",
      "reason": "approval",
      "formId": null,
      "metadata": { "entity": "order", "id": "123" },
      "timeout": "72h",
      "timeoutMs": 259200000
    }
  }
}
```

## Scripts útiles

- `npm run build` – compila a `dist/`
- `npm test` – ejecuta tests

## Licencia

MIT