UNPKG

arvo-event-handler

Version:

Type-safe event handler system with versioning, telemetry, and contract validation for distributed Arvo event-driven architectures, featuring routing and multi-handler support.

161 lines (160 loc) 7.82 kB
"use strict"; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); var arvo_core_1 = require("arvo-core"); /** * Represents an ArvoMachine object that can be consumed by an Arvo orchestrator. * ArvoMachine encapsulates the logic and metadata required for an Arvo-compatible * state machine. It combines XState's actor logic with Arvo-specific contracts * and versioning information. * * @remarks * It is strongly recommended to use `setupArvoMachine(...).createMachine(...)` * instead of creating this object directly. The setup function provides additional * type safety and validation that helps prevent runtime errors. */ var ArvoMachine = /** @class */ (function () { /** * Creates a new ArvoMachine instance. * * @param id - A unique identifier for the machine. This ID must be unique within * the scope of an orchestrator and is used for routing and logging. * * @param version - The semantic version of the machine. Must follow semver format * and match the version specified in the contract. * * @param contracts - Configuration object containing contract definitions * @param contracts.self - The contract defining this machine's interface and capabilities * @param contracts.services - Record of contracts for services this machine can interact with * * @param logic - The XState actor logic that defines the machine's behavior, * including states, transitions, and actions. * @param [requiresResourceLocking] - Optional flag indicating if the machine needs distributed locks. * False when machine has no parallel states and executes sequentially. * Defaults to true. * * @throws {Error} When contracts are invalid or incompatible with the specified version */ function ArvoMachine(id, version, contracts, logic, requiresResourceLocking) { if (requiresResourceLocking === void 0) { requiresResourceLocking = true; } this.id = id; this.version = version; this.contracts = contracts; this.logic = logic; this.requiresResourceLocking = requiresResourceLocking; } Object.defineProperty(ArvoMachine.prototype, "source", { /** * Gets the event type that this machine accepts, as defined in its contract. */ get: function () { return this.contracts.self.accepts.type; }, enumerable: false, configurable: true }); /** * Validates an event against the machine's contracts and data schemas. * Performs validation for both self-contract events and service contract events. * * @param event - The event to validate * @returns A validation result object: * - "VALID" - Event is valid and can be processed * - "CONTRACT_UNRESOLVED" - No matching contract found for the event * - "INVALID" - Event dataschema conflict with contract * - "INVALID_DATA" - Event data conflicts with contract * * @example * ```typescript * const result = machine.validateInput(event); * if (result.type === "VALID") { * // Process the event * } else if (result.type === "INVALID") { * console.error(result.error); * } else { * // Handle unresolved contract * } * ``` * * @remarks * The validation process includes: * - Finding a matching contract (self or service) * - Validating dataschema URI and version if present * - Validating event data against the contract schema */ ArvoMachine.prototype.validateInput = function (event) { var _a, _b, _c; var resovledContract = null; var contractType; if (event.type === this.contracts.self.accepts.type) { resovledContract = this.contracts.self; contractType = 'self'; } else { resovledContract = (_a = Object.fromEntries(Object.values(this.contracts.services).reduce(function (acc, cur) { return __spreadArray(__spreadArray([], acc, true), __spreadArray(__spreadArray([], cur.emitList, true), [cur.systemError], false).map(function (item) { return [item.type, cur]; }), true); }, []))[event.type]) !== null && _a !== void 0 ? _a : null; contractType = 'service'; } if (!resovledContract) { (0, arvo_core_1.logToSpan)({ level: 'WARNING', message: "Contract resolution failed: No matching contract found for event (id='".concat(event.id, "', type='").concat(event.type, "')"), }); return { type: 'CONTRACT_UNRESOLVED', }; } (0, arvo_core_1.logToSpan)({ level: 'INFO', message: "Contract resolved: Contract(uri='".concat(resovledContract.uri, "', version='").concat(resovledContract.version, "', type='").concat(resovledContract.accepts.type, "') for the event(id='").concat(event.id, "', type='").concat(event.type, "')"), }); var dataschema = arvo_core_1.EventDataschemaUtil.parse(event); if (!dataschema) { (0, arvo_core_1.logToSpan)({ level: 'WARNING', message: "Dataschema resolution failed: Unable to parse dataschema='".concat(event.dataschema, "' for event(id='").concat(event.id, "', type='").concat(event.type, "')"), }); } else { (0, arvo_core_1.logToSpan)({ level: 'INFO', message: "Dataschema resolved: ".concat(event.dataschema, " matches contract(uri='").concat(resovledContract.uri, "', version='").concat(resovledContract.version, "')"), }); if (dataschema.uri !== resovledContract.uri) { return { type: 'INVALID', error: new Error("Contract URI mismatch: ".concat(contractType, " Contract(uri='").concat(resovledContract.uri, "', type='").concat(resovledContract.accepts.type, "') does not match Event(dataschema='").concat(event.dataschema, "', type='").concat(event.type, "')")), }; } if (!(0, arvo_core_1.isWildCardArvoSematicVersion)(dataschema.version) && dataschema.version !== resovledContract.version) { return { type: 'INVALID', error: new Error("Contract version mismatch: ".concat(contractType, " Contract(version='").concat(resovledContract.version, "', type='").concat(resovledContract.accepts.type, "', uri=").concat(resovledContract.uri, ") does not match Event(dataschema='").concat(event.dataschema, "', type='").concat(event.type, "')")), }; } } var validationSchema = contractType === 'self' ? resovledContract.accepts.schema : ((_b = resovledContract.emits[event.type]) !== null && _b !== void 0 ? _b : resovledContract.systemError.schema); var error = (_c = validationSchema.safeParse(event.data).error) !== null && _c !== void 0 ? _c : null; if (error) { return { type: 'INVALID_DATA', error: error, }; } return { type: 'VALID', }; }; return ArvoMachine; }()); exports.default = ArvoMachine;