UNPKG

@hemia/workflow-core

Version:

Core del sistema de orquestación de workflows de Hemia. Permite definir, ejecutar y probar flujos personalizados.

296 lines (210 loc) 7.06 kB
# @hemia/workflow-core `@hemia/workflow-core` es el núcleo de un sistema de orquestación de flujos (workflows), pensado para ser modular, extensible y fácilmente integrable. Define los tipos, utilidades y herramientas necesarias para ejecutar, probar e interpolar pasos dentro de un workflow. --- ## 🚀 Características * Definición estructurada de workflows y pasos * Soporte para triggers manuales, por webhook y programados (cron) * Evaluación dinámica de parámetros mediante expresiones (`jexl`) * Utilidades para manejar contexto de ejecución, variables y logs * Herramientas para pruebas unitarias de nodos * Mecanismo de salida e interpolación entre pasos --- ## 📦 Instalación ```bash npm install @hemia/workflow-core ``` --- ## 🧩 Estructura de un Workflow ```ts const workflow: Workflow = { id: 'order-approval', name: 'Aprobación de Pedido', trigger: { type: 'manual' }, steps: [ { id: 'start', name: 'Inicio', type: 'noop', next: [{ id: 'validate' }], params: {}, }, { id: 'validate', name: 'Validar Pedido', type: 'http', next: [{ id: 'log' }], params: { method: 'POST', url: 'https://api.example.com/validate', body: { orderId: '{{variables.orderId}}' } } }, { id: 'log', name: 'Registrar resultado', type: 'log', params: { message: 'Resultado: {{output_validate.status}}' } } ], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; ``` --- ## ⚙️ Tipos clave ### `Workflow` Define el flujo general, los pasos y cómo se activa (manual, webhook o cron). ### `WorkflowStep` Cada paso define su tipo, parámetros (`params`), y los pasos siguientes (`next`). ### `ExecutionContext` Contexto disponible durante la ejecución. Permite: * Guardar y obtener variables * Registrar logs * Recuperar resultados (`output_{{stepId}}`) --- ## 🔧 Utilidades principales ### 📌 `interpolate(template, context)` Evalúa expresiones dentro de strings usando `{{}}` y el contexto de ejecución: ```ts await interpolate('Pedido ID: {{variables.orderId}}', context); ``` ### 🧮 `interpolateStepParams(params, context)` Evalúa todos los valores de `params` dinámicamente: ```ts const resolved = await interpolateStepParams(step.params, context); ``` ### 📤 `setOutput(stepId, data, context)` Guarda el resultado de un paso: ```ts setOutput('validate', { status: 'approved' }, context); ``` ### 📥 `getOutput(stepId, context)` Obtiene la salida de un paso anterior: ```ts const result = getOutput('validate', context); ``` ### 📚 `contextHelpers` Funciones auxiliares para variables y logs: ```ts setVar('foo', 123, context); const value = getVar('foo', context, 0); logInfo(context, 'Mensaje informativo'); ``` --- ## 🔧 Interpolación avanzada: ${config.*} y transforms JEXL El core soporta interpolación de: - Variables con `{{ ... }}` usando JEXL sobre `context.variables`. - Placeholders de configuración `${config.*}` en strings. El engine inyecta `variables.config` desde `workflow.config` (o `workflow.metadata.config`). Ejemplos: ```ts // String con ${config.*} const url = "${config.apiUrl}/vacations"; // resuelve desde variables.config.apiUrl // Expresiones JEXL "{{ input.employeeId }}" "{{ output_checkVacationBalance.data.availableDays }}" ``` ### Transforms de JEXL (funciones en templates con pipe) Puedes registrar funciones globales para usarlas en templates con la sintaxis de pipe: ```ts import { registerJexlTransforms, addJexlTransform } from '@hemia/workflow-core'; registerJexlTransforms({ getManagerEmail: (employeeId: string) => lookupEmail(employeeId), generateApprovalUrl: (requestId: string) => `${process.env.APP_URL}/approve/${requestId}`, }); // O individualmente addJexlTransform('safeLower', (s: any) => (s ?? '').toString().toLowerCase()); ``` Uso en templates: ```jsonc { "to": "{{ input.employeeId | getManagerEmail }}", "approvalUrl": "{{ input.requestId | generateApprovalUrl }}", "slug": "{{ input.name | safeLower }}" } ``` Notas: - Los transforms son globales al proceso y no requieren inyección por ejecución. - Mantén las funciones puras y rápidas; se ejecutan durante la interpolación. - `${config.*}` solo resuelve claves bajo `config`. --- ## 🧪 Testing Puedes testear tus nodos individualmente usando `testNode`: ```ts import { testNode } from '@hemia/workflow-core'; const myNode: WorkflowNode = { type: 'noop', async execute(params, context) { return { success: true, output: { test: true } }; } }; const { result, context } = await testNode(myNode, { foo: '{{1 + 1}}' }); ``` --- ## 🧱 Extender con nodos personalizados ```ts const CustomNode: WorkflowNode = { type: 'custom', async execute(params, context) { context.log('Ejecutando nodo personalizado'); return { success: true, output: { done: true } }; } }; ``` --- ## 🧨 Manejo de errores * `WorkflowValidationError`: estructura inválida del workflow * `WorkflowExecutionError`: error durante la ejecución de un paso * `ExecutionResult.error`: detalles específicos del nodo --- ## 🧰 Exports ```ts // Tipos import { Workflow, WorkflowStep, ExecutionContext, WorkflowNode } from '@hemia/workflow-core'; // Utilidades de interpolación y contexto import { interpolate, interpolateStepParams, interpolateOutput, setOutput, getOutput, contextHelpers, testNode, MockExecutionContext } from '@hemia/workflow-core'; ``` --- --- ## 🧰 Utilidades de inspección y evaluación de expresiones Estas funciones ayudan a detectar y trabajar con expresiones interpoladas y condiciones dentro de los workflows. ### Detección de expresiones interpolables - **isInterpolatable(value): boolean** Indica si un valor puede contener expresiones interpoladas (`string`, `array` u `object`). - **isStringInterpolatable(value): boolean** Indica si un string contiene una expresión `{{ ... }}`. - **isArrayInterpolatable(value): boolean** Indica si algún elemento de un array es interpolable. - **isObjectInterpolatable(value): boolean** Indica si algún valor de un objeto es interpolable. - **isTemplateInterpolatable(template): boolean** Indica si un template (string, array u objeto) es interpolable. - **isVariableExpression(expression): boolean** Indica si un string es una expresión de variable simple, como `{{foo.bar}}`. #### Ejemplo ```ts isInterpolatable({ foo: '{{bar}}' }); // true isStringInterpolatable('Hola {{nombre}}'); // true isArrayInterpolatable(['a', '{{b}}']); // true isObjectInterpolatable({ a: 1, b: '{{x}}' }); // true isTemplateInterpolatable(['{{x}}', 2]); // true isVariableExpression('{{foo.bar}}'); // true ``` -- ## 🧠 Contribuye ¿Quieres agregar nodos, validaciones o mejoras al motor de ejecución? Este paquete es el corazón del sistema, y puede ser extendido fácilmente. --- ## 📄 Licencia MIT © Hemia Technologies