UNPKG

arvo-event-handler

Version:

A complete set of orthogonal event handler and orchestration primitives for Arvo based applications, featuring declarative state machines (XState), imperative resumables for agentic workflows, contract-based routing, OpenTelemetry observability, and in-me

207 lines (206 loc) 11.5 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.handleOrchestrationErrors = exports.createSystemErrorEvents = void 0; var api_1 = require("@opentelemetry/api"); var arvo_core_1 = require("arvo-core"); var ArvoDomain_1 = require("../ArvoDomain"); var errors_1 = require("../errors"); var utils_1 = require("../utils"); var orchestrationExecutionState_1 = require("./orchestrationExecutionState"); var types_1 = require("./types"); /** * Creates standardized system error events for orchestration failures. * * Generates error events that route back to the workflow initiator, preserving * tracing context and orchestration hierarchy. Supports multiple domains for * error distribution. * * @param params - Error event creation parameters * @returns Array of system error events for each configured domain */ var createSystemErrorEvents = function (_a) { var _b, _c, _d, _e, _f; var error = _a.error, event = _a.event, otelHeaders = _a.otelHeaders, _orchestrationParentSubject = _a.orchestrationParentSubject, initEventId = _a.initEventId, selfContract = _a.selfContract, systemErrorDomain = _a.systemErrorDomain, executionunits = _a.executionunits, source = _a.source, handlerType = _a.handlerType; // In case of none transaction errors like errors from // the machine or the event creation etc, the are workflow // error and shuold be handled by the workflow. Then are // called system error and must be sent // to the initiator. In as good of a format as possible var parsedEventSubject = null; var orchestrationParentSubject = null; if (handlerType === 'orchestrator' || handlerType === 'resumable') { orchestrationParentSubject = _orchestrationParentSubject; try { parsedEventSubject = arvo_core_1.ArvoOrchestrationSubject.parse(event.subject); } catch (e) { (0, arvo_core_1.logToSpan)({ level: 'WARNING', message: "Unable to parse event subject: ".concat(e.message), }); } } var domainSets = systemErrorDomain.map(function (item) { return (0, ArvoDomain_1.resolveEventDomain)({ parentSubject: orchestrationParentSubject, currentSubject: event.subject, domainToResolve: item, handlerSelfContract: selfContract, eventContract: selfContract, triggeringEvent: event, }); }); var result = []; for (var _i = 0, _g = Array.from(new Set(domainSets)); _i < _g.length; _i++) { var _dom = _g[_i]; var factoryBuilder = handlerType === 'handler' ? arvo_core_1.createArvoEventFactory : arvo_core_1.createArvoOrchestratorEventFactory; result.push(factoryBuilder(selfContract).systemError({ source: source, // If the initiator of the workflow exist then match the // subject so that it can incorporate it in its state. If // parent does not exist then this is the root workflow so // use its own subject subject: orchestrationParentSubject !== null && orchestrationParentSubject !== void 0 ? orchestrationParentSubject : event.subject, // The system error must always go back to // the source which initiated it to: (_c = (_b = parsedEventSubject === null || parsedEventSubject === void 0 ? void 0 : parsedEventSubject.execution) === null || _b === void 0 ? void 0 : _b.initiator) !== null && _c !== void 0 ? _c : event.source, error: error, traceparent: (_d = otelHeaders.traceparent) !== null && _d !== void 0 ? _d : undefined, tracestate: (_e = otelHeaders.tracestate) !== null && _e !== void 0 ? _e : undefined, accesscontrol: (_f = event.accesscontrol) !== null && _f !== void 0 ? _f : undefined, executionunits: executionunits, // If there is initEventID then use that. // Otherwise, use event id. If the error is in init event // then it will be the same as initEventId. Otherwise, // we still would know what cause this error parentid: initEventId !== null && initEventId !== void 0 ? initEventId : event.id, domain: _dom, })); } return result; }; exports.createSystemErrorEvents = createSystemErrorEvents; /** * Handles errors during orchestration execution with proper state management. * * Processes errors by determining if they are violations (retriable) or execution * errors (terminal). For execution errors, persists failure state and generates * system error events. For violations, returns the error to be thrown without * state persistence. * * @returns Either the violation error to throw or system error events to emit */ var handleOrchestrationErrors = function (_handlerType, param, metadata, span) { return __awaiter(void 0, void 0, void 0, function () { var handlerType, error, errorEvents, _i, _a, _b, errEvtIdx, errEvt, _c, _d, _e, key, value; return __generator(this, function (_f) { switch (_f.label) { case 0: handlerType = types_1.ArvoOrchestrationHandlerMap[_handlerType]; error = (0, utils_1.isError)(param.error) ? param.error : new errors_1.ExecutionViolation("Non-Error object thrown during machine execution: ".concat(typeof param.error, ". This indicates a serious implementation flaw.")); (0, arvo_core_1.exceptionToSpan)(error, span); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message, }); // Don't persist state on a violation // // A violation means that there is something // wrong in the state persitance or it is a // error which will be handled outside the // Arvo mechanism. So, it makes sense that it // does not impact the state. The violations // can be used to trigger retries as well if ((0, arvo_core_1.isViolationError)(error)) { (0, arvo_core_1.logToSpan)({ level: 'CRITICAL', message: "".concat(handlerType || 'Arvo orchestration handler', " violation error: ").concat(error.message), }); return [2 /*return*/, { errorToThrow: error, events: null, }]; } return [4 /*yield*/, param.syncEventResource .persistState(param.event, { __type: 'OrchestrationExecutionMemoryRecord', executionStatus: orchestrationExecutionState_1.OrchestrationExecutionStatus.FAILURE, subject: param.event.subject, error: param.error, }, null, metadata, span) .catch(function (e) { (0, arvo_core_1.logToSpan)({ level: 'CRITICAL', message: "Error in orchestrator persisting the failure state: ".concat(e.message), }); })]; case 1: _f.sent(); (0, arvo_core_1.logToSpan)({ level: 'ERROR', message: "".concat(handlerType || 'Arvo orchestration handler', " execution failed: ").concat(error.message), }); errorEvents = (0, exports.createSystemErrorEvents)(__assign(__assign({}, param), { error: error })); for (_i = 0, _a = Object.entries(errorEvents); _i < _a.length; _i++) { _b = _a[_i], errEvtIdx = _b[0], errEvt = _b[1]; for (_c = 0, _d = Object.entries(errEvt.otelAttributes); _c < _d.length; _c++) { _e = _d[_c], key = _e[0], value = _e[1]; span.setAttribute("emittables.".concat(errEvtIdx, ".").concat(key), value); } } return [2 /*return*/, { errorToThrow: null, events: errorEvents, }]; } }); }); }; exports.handleOrchestrationErrors = handleOrchestrationErrors;