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
JavaScript
;
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;