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
154 lines (153 loc) • 9.44 kB
JavaScript
;
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 __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));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupArvoMachine = setupArvoMachine;
var arvo_core_1 = require("arvo-core");
var xstate_1 = require("xstate");
var _1 = __importDefault(require("."));
var servicesValidation_1 = require("../ArvoOrchestrationUtils/servicesValidation");
var errors_1 = require("../errors");
var object_1 = require("../utils/object");
var utils_1 = require("./utils");
/**
* Establishes the foundation for creating Arvo-compatible state machines.
*
* Designed for synchronous state machine orchestrations in Arvo's event-driven architecture.
* Builds upon XState with Arvo-specific constraints to enforce predictable state transitions.
*
* @throws {ConfigViolation} When configuration violates Arvo constraints:
* - Using `actors` or `delays` (async behavior not supported)
* - Overriding reserved `enqueueArvoEvent` action name
* - Machine version mismatch with contract version
* - Using `invoke` or `after` in state configurations
* - Service contracts with duplicate URIs (multiple versions of same contract)
* - Circular dependency (self contract URI matches a service contract URI)
*/
function setupArvoMachine(param) {
var _a, _b;
var createConfigErrorMessage = function (type) {
return (0, arvo_core_1.cleanString)("\n Configuration Error: '".concat(type, "' not supported in Arvo machines\n \n Arvo machines do not support XState ").concat(type === 'actor' ? 'actors' : 'delay transitions', " as they introduce asynchronous behavior.\n \n To fix:\n 1. Remove the '").concat(type, "' configuration\n 2. Use Arvo's event-driven patterns instead for asynchronous operations\n "));
};
if (param.actors) {
throw new errors_1.ConfigViolation(createConfigErrorMessage('actor'));
}
if (param.delays) {
throw new errors_1.ConfigViolation(createConfigErrorMessage('delays'));
}
if ((_a = param.actions) === null || _a === void 0 ? void 0 : _a.enqueueArvoEvent) {
throw new errors_1.ConfigViolation((0, arvo_core_1.cleanString)("\n Configuration Error: Reserved action name 'enqueueArvoEvent'\n \n 'enqueueArvoEvent' is an internal Arvo system action and cannot be overridden.\n \n To fix: Use a different name for your action, such as:\n - 'queueCustomEvent'\n - 'scheduleEvent'\n - 'dispatchEvent'\n "));
}
(0, servicesValidation_1.servicesValidation)(param.contracts, 'machine');
var combinedActions = __assign(__assign({}, ((_b = param.actions) !== null && _b !== void 0 ? _b : {})), { enqueueArvoEvent: (0, xstate_1.assign)(function (_a, param) {
var _b, _c, _d, _e, _f;
var context = _a.context;
return (__assign(__assign({}, (context !== null && context !== void 0 ? context : {})), { arvo$$: __assign(__assign({}, ((_b = context === null || context === void 0 ? void 0 : context.arvo$$) !== null && _b !== void 0 ? _b : {})), { volatile$$: __assign(__assign({}, ((_d = (_c = context === null || context === void 0 ? void 0 : context.arvo$$) === null || _c === void 0 ? void 0 : _c.volatile$$) !== null && _d !== void 0 ? _d : {})), { eventQueue$$: __spreadArray(__spreadArray([], (((_f = (_e = context === null || context === void 0 ? void 0 : context.arvo$$) === null || _e === void 0 ? void 0 : _e.volatile$$) === null || _f === void 0 ? void 0 : _f.eventQueue$$) || []), true), [param], false) }) }) }));
}) });
// Call the original setup function with modified parameters
var systemSetup = (0, xstate_1.setup)({
schemas: param.schemas,
types: param.types,
guards: param.guards,
actions: combinedActions,
});
/**
* Creates an Arvo-compatible XState machine.
*/
var createMachine = function (config) {
var _a, _b, _c, _d, _e;
var machineVersion = (_a = config.version) !== null && _a !== void 0 ? _a : param.contracts.self.version;
if (machineVersion !== param.contracts.self.version) {
throw new errors_1.ConfigViolation("Version mismatch: Machine version must be '".concat(param.contracts.self.version, "' or undefined, received '").concat(config.version, "'"));
}
var createConfigErrorMessage = function (type, path) {
var location = path.join(' > ');
if (type === 'invoke') {
return (0, arvo_core_1.cleanString)("\n Configuration Error: 'invoke' not supported\n \n Location: ".concat(location, "\n \n Arvo machines do not support XState invocations as they introduce asynchronous behavior.\n \n To fix: Replace 'invoke' with Arvo event-driven patterns for asynchronous operations\n "));
}
if (type === 'after') {
return (0, arvo_core_1.cleanString)("\n Configuration Error: 'after' not supported\n \n Location: ".concat(location, "\n \n Arvo machines do not support delayed transitions as they introduce asynchronous behavior.\n \n To fix: Replace 'after' with Arvo event-driven patterns for time-based operations\n "));
}
if (type === 'enqueueArvoEvent') {
return (0, arvo_core_1.cleanString)("\n Configuration Error: Reserved action name 'enqueueArvoEvent'\n \n Location: ".concat(location, "\n \n 'enqueueArvoEvent' is an internal Arvo system action and cannot be used in machine configurations.\n \n To fix: Use a different name for your action\n "));
}
};
for (var _i = 0, _f = (0, object_1.getAllPaths)((_b = config.states) !== null && _b !== void 0 ? _b : {}); _i < _f.length; _i++) {
var item = _f[_i];
if (item.path.includes('invoke')) {
throw new errors_1.ConfigViolation((_c = createConfigErrorMessage('invoke', item.path)) !== null && _c !== void 0 ? _c : 'Invoke not allowed');
}
if (item.path.includes('after')) {
throw new errors_1.ConfigViolation((_d = createConfigErrorMessage('after', item.path)) !== null && _d !== void 0 ? _d : 'After not allowed');
}
if (item.path.includes('enqueueArvoEvent')) {
throw new errors_1.ConfigViolation((_e = createConfigErrorMessage('enqueueArvoEvent', item.path)) !== null && _e !== void 0 ? _e : 'EnqueueArvoEvent not allowed');
}
}
var machine = systemSetup.createMachine(__assign({}, config));
var hasParallelStates = (0, utils_1.detectParallelStates)(machine.config);
var hasMultipleNonSystemErrorEvents = Object.values(param.contracts.services).some(function (item) { return Object.keys(item.emits).length > 1; });
var requiresLocking = hasParallelStates || hasMultipleNonSystemErrorEvents;
return new _1.default(config.id, machineVersion, param.contracts, machine, requiresLocking);
};
return {
/**
* Creates an Arvo-compatible state machine with the specified configuration.
*
* Constructs a fully-typed state machine that orchestrates event-driven workflows
* using the contracts and types defined in setup. The machine enforces synchronous
* execution and validates configuration against Arvo constraints.
*
* For more information, see [xstate state machine docs](https://stately.ai/docs/states)
* @returns {ArvoMachine} A configured Arvo machine ready for execution
* @throws {ConfigViolation} When configuration violates Arvo constraints (see {@link setupArvoMachine} docs)
*
* @example
* ```typescript
* const machine = setup.createMachine({
* id: 'machineV100',
* initial: 'verifying',
* context: ({ input }) => ({
* userId: input.data.userId,
* verified: false
* }),
* states: {
* verifying: {
* on: {
* 'com.user.verified': {
* target: 'active',
* actions: { type: 'updateUser' }
* }
* }
* },
* active: {
* type: 'final'
* }
* }
* });
* ```
*/
createMachine: createMachine,
};
}