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

250 lines (249 loc) 17.3 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.ArvoOrchestrator = void 0; var api_1 = require("@opentelemetry/api"); var arvo_core_1 = require("arvo-core"); var createEmitableEvent_1 = require("../ArvoOrchestrationUtils/createEmitableEvent"); var orchestrationExecutionWrapper_1 = require("../ArvoOrchestrationUtils/orchestrationExecutionWrapper"); var SyncEventResource_1 = require("../SyncEventResource"); var errors_1 = require("../errors"); var ArvoDomain_1 = require("../ArvoDomain"); var orchestrationExecutionState_1 = require("../ArvoOrchestrationUtils/orchestrationExecutionState"); /** * Orchestrates state machine execution and lifecycle management. * * Coordinates machine resolution, state persistence, event processing, and error handling * for Arvo's event-driven orchestration workflows. Manages the complete lifecycle from * event receipt through machine execution to emitting result events. */ var ArvoOrchestrator = /** @class */ (function () { function ArvoOrchestrator(_a) { var _b; var executionunits = _a.executionunits, memory = _a.memory, registry = _a.registry, executionEngine = _a.executionEngine, requiresResourceLocking = _a.requiresResourceLocking, defaultEventEmissionDomains = _a.defaultEventEmissionDomains, spanOptions = _a.spanOptions; var _c, _d, _e, _f, _g, _h, _j; this.executionunits = executionunits !== null && executionunits !== void 0 ? executionunits : 0; this.registry = registry; this.executionEngine = executionEngine; this.syncEventResource = new SyncEventResource_1.SyncEventResource(memory, requiresResourceLocking); this.defaultEventEmissionDomains = __assign({ systemError: [ArvoDomain_1.ArvoDomain.ORCHESTRATION_CONTEXT], services: [ArvoDomain_1.ArvoDomain.LOCAL], complete: [ArvoDomain_1.ArvoDomain.ORCHESTRATION_CONTEXT] }, (defaultEventEmissionDomains !== null && defaultEventEmissionDomains !== void 0 ? defaultEventEmissionDomains : {})); this.spanOptions = __assign(__assign({ kind: api_1.SpanKind.PRODUCER }, spanOptions), { attributes: __assign(__assign((_b = {}, _b[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = arvo_core_1.ArvoExecutionSpanKind.ORCHESTRATOR, _b[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = arvo_core_1.OpenInferenceSpanKind.CHAIN, _b), ((_c = spanOptions === null || spanOptions === void 0 ? void 0 : spanOptions.attributes) !== null && _c !== void 0 ? _c : {})), { 'arvo.handler.source': this.source, 'arvo.contract.uri': (_j = (_h = (_g = (_f = (_e = (_d = this === null || this === void 0 ? void 0 : this.registry) === null || _d === void 0 ? void 0 : _d.machines) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.contracts) === null || _g === void 0 ? void 0 : _g.self) === null || _h === void 0 ? void 0 : _h.uri) !== null && _j !== void 0 ? _j : 'N/A' }) }); } Object.defineProperty(ArvoOrchestrator.prototype, "source", { /** Source identifier from the first registered machine */ get: function () { return this.registry.machines[0].source; }, enumerable: false, configurable: true }); Object.defineProperty(ArvoOrchestrator.prototype, "requiresResourceLocking", { /** Whether this orchestrator requires resource locking for concurrent safety */ get: function () { return this.syncEventResource.requiresResourceLocking; }, enumerable: false, configurable: true }); Object.defineProperty(ArvoOrchestrator.prototype, "memory", { /** Memory interface for state persistence and retrieval */ get: function () { return this.syncEventResource.memory; }, enumerable: false, configurable: true }); Object.defineProperty(ArvoOrchestrator.prototype, "domain", { /** The contract-defined domain for the handler */ get: function () { return this.registry.machines[0].contracts.self.domain; }, enumerable: false, configurable: true }); /** * Executes state machine orchestration for an incoming event. * * Performs the complete orchestration workflow: resolves the appropriate machine, * validates input, executes the machine logic, processes emitted events, and persists * the new state. Handles both new orchestrations and continuation of existing ones. * * For violation errors (transaction, execution, contract, config), the error is thrown * to enable retry mechanisms. For non-violation errors, system error events are emitted * to the workflow initiator, and the orchestration enters a terminal failure state. * * @param event - The incoming event triggering orchestration * @param opentelemetry - Optional OpenTelemetry configuration for tracing * @returns Object containing emitted events from the orchestration or system errors * * @throws {TransactionViolation} When lock acquisition or state operations fail (retriable) * @throws {ContractViolation} When event data doesn't match contract schema (retriable) * @throws {ConfigViolation} When machine resolution fails or version is missing (retriable) * @throws {ExecutionViolation} When workflow execution encounters critical errors defined by the handler developer */ ArvoOrchestrator.prototype.execute = function (event, opentelemetry) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, (0, orchestrationExecutionWrapper_1.executeWithOrchestrationWrapper)({ _handlerType: 'orchestrator', event: event, opentelemetry: opentelemetry !== null && opentelemetry !== void 0 ? opentelemetry : { inheritFrom: 'EVENT' }, spanOptions: __assign({ spanName: function (_a) { var selfContractUri = _a.selfContractUri, consumedEvent = _a.consumedEvent; return "Orchestrator<".concat(selfContractUri, ">@<").concat(consumedEvent.type, ">"); } }, this.spanOptions), source: this.source, syncEventResource: this.syncEventResource, executionunits: this.executionunits, systemErrorDomain: this.defaultEventEmissionDomains.systemError, selfContract: this.registry.machines[0].contracts.self, }, function (_a) { return __awaiter(_this, [_a], void 0, function (_b) { var machine, inputValidation, executionResult, rawMachineEmittedEvents, i, emittables, newState; var _c, _d, _e, _f, _g, _h; var span = _b.span, otelHeaders = _b.otelHeaders, orchestrationParentSubject = _b.orchestrationParentSubject, initEventId = _b.initEventId, parsedEventSubject = _b.parsedEventSubject, state = _b.state; return __generator(this, function (_j) { (0, arvo_core_1.logToSpan)({ level: 'INFO', message: "Resolving machine for event ".concat(event.type), }, span); machine = this.registry.resolve(event, { inheritFrom: 'CONTEXT' }); if (!machine) { throw new errors_1.ConfigViolation("Machine resolution failed: No machine found matching orchestrator name='".concat(parsedEventSubject.orchestrator.name, "' and version='").concat(parsedEventSubject.orchestrator.version, "'.")); } (0, arvo_core_1.logToSpan)({ level: 'INFO', message: "Input validation started for event ".concat(event.type, " on machine ").concat(machine.source), }, span); inputValidation = machine.validateInput(event, span); if (inputValidation.type === 'CONTRACT_UNRESOLVED') { throw new errors_1.ConfigViolation('Contract validation failed - Event does not match any registered contract schemas in the machine'); } if (inputValidation.type === 'INVALID_DATA' || inputValidation.type === 'INVALID') { throw new errors_1.ContractViolation("Input validation failed - Event data does not meet contract requirements: ".concat(inputValidation.error.message)); } executionResult = this.executionEngine.execute({ state: (_c = state === null || state === void 0 ? void 0 : state.state) !== null && _c !== void 0 ? _c : null, event: event, machine: machine }, { inheritFrom: 'CONTEXT' }); span.setAttribute('arvo.orchestration.status', executionResult.state.status); rawMachineEmittedEvents = executionResult.events; // For all the service events (non final output) make sure // that the default domain is this.defaultEventEmissionDomains.services. // This is because the assumption is that all the normal services // the orchestrator usually talks to are in the same local // domain. for (i = 0; i < rawMachineEmittedEvents.length; i++) { rawMachineEmittedEvents[i].domain = (_d = rawMachineEmittedEvents[i].domain) !== null && _d !== void 0 ? _d : this.defaultEventEmissionDomains.services; } if (executionResult.finalOutput) { rawMachineEmittedEvents.push({ type: machine.contracts.self.metadata.completeEventType, id: executionResult.finalOutput.__id, data: executionResult.finalOutput, to: (_f = (_e = parsedEventSubject.meta) === null || _e === void 0 ? void 0 : _e.redirectto) !== null && _f !== void 0 ? _f : parsedEventSubject.execution.initiator, domain: (_g = executionResult.finalOutput.__domain) !== null && _g !== void 0 ? _g : this.defaultEventEmissionDomains.complete, executionunits: executionResult.finalOutput.__executionunits, }); } emittables = (0, createEmitableEvent_1.processRawEventsIntoEmittables)({ rawEvents: rawMachineEmittedEvents, otelHeaders: otelHeaders, orchestrationParentSubject: orchestrationParentSubject, sourceEvent: event, selfContract: machine.contracts.self, serviceContracts: machine.contracts.services, initEventId: initEventId, executionunits: this.executionunits, source: this.source, }, span); (0, arvo_core_1.logToSpan)({ level: 'INFO', message: "Machine execution completed - Status: ".concat(executionResult.state.status, ", Generated events: ").concat(emittables.length), }, span); newState = { __type: 'OrchestrationExecutionMemoryRecord', executionStatus: executionResult.finalOutput ? orchestrationExecutionState_1.OrchestrationExecutionStatus.DONE : orchestrationExecutionState_1.OrchestrationExecutionStatus.NORMAL, initEventId: initEventId, subject: event.subject, parentSubject: orchestrationParentSubject, status: executionResult.state.status, value: (_h = executionResult.state.value) !== null && _h !== void 0 ? _h : null, state: executionResult.state, events: { consumed: event.toJSON(), produced: emittables.map(function (item) { return item.toJSON(); }), }, machineDefinition: JSON.stringify(machine.logic.config), }; return [2 /*return*/, { emittables: emittables, newState: newState }]; }); }); })]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }; Object.defineProperty(ArvoOrchestrator.prototype, "systemErrorSchema", { /** * Provides access to the system error event schema configuration. */ get: function () { return { type: this.registry.machines[0].contracts.self.systemError.type, schema: arvo_core_1.ArvoErrorSchema, }; }, enumerable: false, configurable: true }); return ArvoOrchestrator; }()); exports.ArvoOrchestrator = ArvoOrchestrator;