arvo-event-handler 3.0.14 → 3.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ArvoEventHandler/index.d.ts +20 -123
- package/dist/ArvoEventHandler/index.js +5 -112
- package/dist/ArvoMachine/createMachine.d.ts +103 -158
- package/dist/ArvoMachine/createMachine.js +59 -181
- package/dist/ArvoMachine/index.d.ts +5 -57
- package/dist/ArvoMachine/index.js +8 -117
- package/dist/ArvoMachine/types.d.ts +0 -82
- package/dist/ArvoMachine/utils.d.ts +0 -15
- package/dist/ArvoMachine/utils.js +0 -15
- package/dist/ArvoOrchestrationUtils/createEmitableEvent.d.ts +31 -0
- package/dist/ArvoOrchestrationUtils/createEmitableEvent.js +21 -3
- package/dist/ArvoOrchestrationUtils/error.d.ts +22 -0
- package/dist/ArvoOrchestrationUtils/error.js +21 -0
- package/dist/ArvoOrchestrationUtils/handlerErrors.d.ts +30 -2
- package/dist/ArvoOrchestrationUtils/handlerErrors.js +19 -2
- package/dist/ArvoOrchestrationUtils/inputValidation.d.ts +47 -0
- package/dist/ArvoOrchestrationUtils/inputValidation.js +120 -0
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionState.d.ts +24 -1
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionState.js +9 -1
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/acquireLockWithValidation.d.ts +5 -1
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/acquireLockWithValidation.js +5 -1
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/index.d.ts +44 -5
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/index.js +17 -5
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/validateAndParseSubject.d.ts +6 -1
- package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/validateAndParseSubject.js +6 -1
- package/dist/ArvoOrchestrationUtils/servicesValidation.d.ts +14 -6
- package/dist/ArvoOrchestrationUtils/servicesValidation.js +14 -6
- package/dist/ArvoOrchestrator/factory.d.ts +18 -15
- package/dist/ArvoOrchestrator/factory.js +19 -16
- package/dist/ArvoOrchestrator/index.d.ts +51 -19
- package/dist/ArvoOrchestrator/index.js +29 -17
- package/dist/ArvoOrchestrator/types.d.ts +74 -49
- package/dist/ArvoResumable/factory.d.ts +31 -36
- package/dist/ArvoResumable/factory.js +29 -18
- package/dist/ArvoResumable/index.d.ts +69 -65
- package/dist/ArvoResumable/index.js +49 -92
- package/dist/ArvoResumable/types.d.ts +177 -72
- package/dist/IArvoEventHandler/index.d.ts +29 -15
- package/package.json +2 -2
|
@@ -46,59 +46,31 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
46
46
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
50
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
51
|
-
if (ar || !(i in from)) {
|
|
52
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
53
|
-
ar[i] = from[i];
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
57
|
-
};
|
|
58
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
50
|
exports.ArvoResumable = void 0;
|
|
60
51
|
var api_1 = require("@opentelemetry/api");
|
|
61
52
|
var arvo_core_1 = require("arvo-core");
|
|
62
53
|
var createEmitableEvent_1 = require("../ArvoOrchestrationUtils/createEmitableEvent");
|
|
54
|
+
var inputValidation_1 = require("../ArvoOrchestrationUtils/inputValidation");
|
|
63
55
|
var orchestrationExecutionWrapper_1 = require("../ArvoOrchestrationUtils/orchestrationExecutionWrapper");
|
|
64
56
|
var index_1 = require("../SyncEventResource/index");
|
|
65
57
|
var errors_1 = require("../errors");
|
|
66
58
|
/**
|
|
67
|
-
* ArvoResumable
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* contract validation throughout the execution lifecycle.
|
|
73
|
-
*
|
|
74
|
-
* This class addresses fundamental issues in event-driven architecture including:
|
|
75
|
-
* - Contract management with runtime validation and type safety
|
|
76
|
-
* - Graduated complexity allowing simple workflows to remain simple
|
|
77
|
-
* - Unified event handling across initialization and service responses
|
|
78
|
-
* - Explicit state management without hidden abstractions
|
|
79
|
-
*
|
|
80
|
-
* Key capabilities:
|
|
81
|
-
* - Handler-based workflow orchestration with explicit state control
|
|
82
|
-
* - Contract-driven event validation with runtime schema enforcement
|
|
83
|
-
* - Distributed resource locking for transaction safety
|
|
84
|
-
* - Comprehensive OpenTelemetry integration for observability
|
|
85
|
-
* - Automatic error handling with system error event generation
|
|
86
|
-
* - Support for orchestrator chaining and nested workflow patterns
|
|
87
|
-
* - Domain-based event routing and organization
|
|
59
|
+
* ArvoResumable complements {@link ArvoOrchestrator} by providing imperative
|
|
60
|
+
* handler functions for orchestration logic instead of declarative state machines.
|
|
61
|
+
* While ArvoOrchestrator excels at complex static workflows with deterministic
|
|
62
|
+
* branching, ArvoResumable handles dynamic orchestrations where branching logic
|
|
63
|
+
* depends on runtime context and event data.
|
|
88
64
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* reduces the learning curve for teams familiar with traditional programming patterns.
|
|
92
|
-
*
|
|
93
|
-
* @see {@link createArvoResumable} Factory function for creating instances
|
|
94
|
-
* @see {@link ArvoResumableHandler} Handler interface documentation
|
|
95
|
-
* @see {@link ArvoResumableState} State structure documentation
|
|
65
|
+
* Use this for dynamic orchestrations with context-dependent branching
|
|
66
|
+
* or when preferring imperative programming patterns over state machines.
|
|
96
67
|
*/
|
|
97
68
|
var ArvoResumable = /** @class */ (function () {
|
|
98
69
|
function ArvoResumable(param) {
|
|
99
70
|
var _a;
|
|
100
71
|
var _b, _c, _d;
|
|
101
|
-
|
|
72
|
+
/** Optional domains for routing system error events */
|
|
73
|
+
this.systemErrorDomain = undefined;
|
|
102
74
|
this.executionunits = param.executionunits;
|
|
103
75
|
this.source = param.contracts.self.type;
|
|
104
76
|
this.syncEventResource = new index_1.SyncEventResource(param.memory, (_b = param.requiresResourceLocking) !== null && _b !== void 0 ? _b : true);
|
|
@@ -108,6 +80,7 @@ var ArvoResumable = /** @class */ (function () {
|
|
|
108
80
|
this.spanOptions = __assign(__assign({ kind: api_1.SpanKind.PRODUCER }, param.spanOptions), { attributes: __assign(__assign((_a = {}, _a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = arvo_core_1.ArvoExecutionSpanKind.RESUMABLE, _a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = arvo_core_1.OpenInferenceSpanKind.CHAIN, _a), ((_d = (_c = param.spanOptions) === null || _c === void 0 ? void 0 : _c.attributes) !== null && _d !== void 0 ? _d : {})), { 'arvo.handler.source': this.source, 'arvo.contract.uri': this.contracts.self.uri }) });
|
|
109
81
|
}
|
|
110
82
|
Object.defineProperty(ArvoResumable.prototype, "requiresResourceLocking", {
|
|
83
|
+
/** Whether this resumable requires resource locking for concurrent safety */
|
|
111
84
|
get: function () {
|
|
112
85
|
return this.syncEventResource.requiresResourceLocking;
|
|
113
86
|
},
|
|
@@ -115,6 +88,7 @@ var ArvoResumable = /** @class */ (function () {
|
|
|
115
88
|
configurable: true
|
|
116
89
|
});
|
|
117
90
|
Object.defineProperty(ArvoResumable.prototype, "memory", {
|
|
91
|
+
/** Memory interface for state persistence and retrieval */
|
|
118
92
|
get: function () {
|
|
119
93
|
return this.syncEventResource.memory;
|
|
120
94
|
},
|
|
@@ -122,72 +96,48 @@ var ArvoResumable = /** @class */ (function () {
|
|
|
122
96
|
configurable: true
|
|
123
97
|
});
|
|
124
98
|
Object.defineProperty(ArvoResumable.prototype, "domain", {
|
|
99
|
+
/** The contract-defined domain for the handler */
|
|
125
100
|
get: function () {
|
|
126
101
|
return this.contracts.self.domain;
|
|
127
102
|
},
|
|
128
103
|
enumerable: false,
|
|
129
104
|
configurable: true
|
|
130
105
|
});
|
|
106
|
+
/**
|
|
107
|
+
* Validates incoming event against self or service contracts.
|
|
108
|
+
*
|
|
109
|
+
* Resolves the appropriate contract (self for initialization, service for responses),
|
|
110
|
+
* validates schema compatibility, and ensures event data matches contract requirements.
|
|
111
|
+
*
|
|
112
|
+
* See {@link validateInputEvent} for more infromation
|
|
113
|
+
*/
|
|
131
114
|
ArvoResumable.prototype.validateInput = function (event, span) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
if (event.type === this.contracts.self.type) {
|
|
140
|
-
contractType = 'self';
|
|
141
|
-
resolvedContract = this.contracts.self.version(parsedEventDataSchema.version);
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
contractType = 'service';
|
|
145
|
-
for (var _i = 0, _b = Object.values(this.contracts.services); _i < _b.length; _i++) {
|
|
146
|
-
var contract = _b[_i];
|
|
147
|
-
if (resolvedContract)
|
|
148
|
-
break;
|
|
149
|
-
for (var _c = 0, _d = __spreadArray(__spreadArray([], contract.emitList, true), [contract.systemError], false); _c < _d.length; _c++) {
|
|
150
|
-
var emitType = _d[_c];
|
|
151
|
-
if (resolvedContract)
|
|
152
|
-
break;
|
|
153
|
-
if (event.type === emitType.type) {
|
|
154
|
-
resolvedContract = contract;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (!resolvedContract) {
|
|
160
|
-
throw new errors_1.ConfigViolation("Contract resolution failed: No matching contract found for event (id='".concat(event.id, "', type='").concat(event.type, "')"));
|
|
161
|
-
}
|
|
162
|
-
(0, arvo_core_1.logToSpan)({
|
|
163
|
-
level: 'INFO',
|
|
164
|
-
message: "Dataschema resolved: ".concat(event.dataschema, " matches contract(uri='").concat(resolvedContract.uri, "', version='").concat(resolvedContract.version, "')"),
|
|
165
|
-
}, span);
|
|
166
|
-
if (parsedEventDataSchema.uri !== resolvedContract.uri) {
|
|
167
|
-
throw new Error("Contract URI mismatch: ".concat(contractType, " Contract(uri='").concat(resolvedContract.uri, "', type='").concat(resolvedContract.accepts.type, "') does not match Event(dataschema='").concat(event.dataschema, "', type='").concat(event.type, "')"));
|
|
168
|
-
}
|
|
169
|
-
if (!(0, arvo_core_1.isWildCardArvoSematicVersion)(parsedEventDataSchema.version) &&
|
|
170
|
-
parsedEventDataSchema.version !== resolvedContract.version) {
|
|
171
|
-
throw new Error("Contract version mismatch: ".concat(contractType, " Contract(version='").concat(resolvedContract.version, "', type='").concat(resolvedContract.accepts.type, "', uri=").concat(resolvedContract.uri, ") does not match Event(dataschema='").concat(event.dataschema, "', type='").concat(event.type, "')"));
|
|
172
|
-
}
|
|
173
|
-
var validationSchema = contractType === 'self'
|
|
174
|
-
? resolvedContract.accepts.schema
|
|
175
|
-
: ((_a = resolvedContract.emits[event.type]) !== null && _a !== void 0 ? _a : resolvedContract.systemError.schema);
|
|
176
|
-
validationSchema.parse(event.data);
|
|
177
|
-
return { contractType: contractType };
|
|
115
|
+
return (0, inputValidation_1.validateInputEvent)({
|
|
116
|
+
event: event,
|
|
117
|
+
selfContract: this.contracts.self,
|
|
118
|
+
serviceContracts: this.contracts.services,
|
|
119
|
+
span: span,
|
|
120
|
+
});
|
|
178
121
|
};
|
|
179
122
|
/**
|
|
180
|
-
* Executes the
|
|
123
|
+
* Executes the workflow handler for an incoming event.
|
|
181
124
|
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
125
|
+
* Processes initialization events or service responses through the versioned handler,
|
|
126
|
+
* manages state persistence, tracks expected events, and generates output events.
|
|
127
|
+
* Workflows in 'done' status ignore subsequent events without processing.
|
|
184
128
|
*
|
|
185
|
-
*
|
|
129
|
+
* For violation errors (transaction, config, contract), the error is thrown to enable
|
|
130
|
+
* retry mechanisms. For non-violation errors, system error events are emitted to the
|
|
131
|
+
* workflow initiator, and the workflow enters a terminal failure state.
|
|
132
|
+
*
|
|
133
|
+
* @param event - The incoming event triggering handler execution
|
|
134
|
+
* @param opentelemetry - Optional OpenTelemetry configuration for tracing
|
|
135
|
+
* @returns Object containing emitted events from the handler or system errors
|
|
186
136
|
*
|
|
187
137
|
* @throws {TransactionViolation} When distributed lock acquisition fails
|
|
188
138
|
* @throws {ConfigViolation} When handler resolution or contract validation fails
|
|
189
139
|
* @throws {ContractViolation} When event schema validation fails
|
|
190
|
-
* @throws {ExecutionViolation} When workflow execution encounters critical errors
|
|
140
|
+
* @throws {ExecutionViolation} When workflow execution encounters critical errors defined by the handler developer
|
|
191
141
|
*/
|
|
192
142
|
ArvoResumable.prototype.execute = function (event, opentelemetry) {
|
|
193
143
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -209,7 +159,7 @@ var ArvoResumable = /** @class */ (function () {
|
|
|
209
159
|
selfContract: this.contracts.self.version('latest'),
|
|
210
160
|
domain: this.domain,
|
|
211
161
|
}, function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
|
|
212
|
-
var contractType, eventTypeToExpectedEvent, _i, _c, _d, _, eventList, _e, eventList_1, _evt, handler, versionedSelfContract, executionResult, rawEvents, emittables, eventTrackingState, newState;
|
|
162
|
+
var inputValidation, contractType, eventTypeToExpectedEvent, _i, _c, _d, _, eventList, _e, eventList_1, _evt, handler, versionedSelfContract, executionResult, rawEvents, emittables, eventTrackingState, newState;
|
|
213
163
|
var _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
214
164
|
var span = _b.span, otelHeaders = _b.otelHeaders, orchestrationParentSubject = _b.orchestrationParentSubject, initEventId = _b.initEventId, parsedEventSubject = _b.parsedEventSubject, state = _b.state;
|
|
215
165
|
return __generator(this, function (_v) {
|
|
@@ -226,7 +176,14 @@ var ArvoResumable = /** @class */ (function () {
|
|
|
226
176
|
level: 'INFO',
|
|
227
177
|
message: "Input validation started for event ".concat(event.type),
|
|
228
178
|
});
|
|
229
|
-
|
|
179
|
+
inputValidation = this.validateInput(event, span);
|
|
180
|
+
if (inputValidation.type === 'CONTRACT_UNRESOLVED') {
|
|
181
|
+
throw new errors_1.ConfigViolation('Contract validation failed - Event does not match any registered contract schemas in the resumable');
|
|
182
|
+
}
|
|
183
|
+
if (inputValidation.type === 'INVALID_DATA' || inputValidation.type === 'INVALID') {
|
|
184
|
+
throw new errors_1.ContractViolation("Input validation failed - Event data does not meet contract requirements: ".concat(inputValidation.error.message));
|
|
185
|
+
}
|
|
186
|
+
contractType = inputValidation.contractType;
|
|
230
187
|
if ((state === null || state === void 0 ? void 0 : state.status) === 'done') {
|
|
231
188
|
(0, arvo_core_1.logToSpan)({
|
|
232
189
|
level: 'INFO',
|
|
@@ -328,7 +285,7 @@ var ArvoResumable = /** @class */ (function () {
|
|
|
328
285
|
};
|
|
329
286
|
Object.defineProperty(ArvoResumable.prototype, "systemErrorSchema", {
|
|
330
287
|
get: function () {
|
|
331
|
-
return this.contracts.self.systemError;
|
|
288
|
+
return __assign(__assign({}, this.contracts.self.systemError), { domain: this.systemErrorDomain });
|
|
332
289
|
},
|
|
333
290
|
enumerable: false,
|
|
334
291
|
configurable: true
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { Span } from '@opentelemetry/api';
|
|
2
|
-
import type { ArvoContract, ArvoEvent, ArvoSemanticVersion, CreateArvoEvent, InferArvoEvent, InferVersionedArvoContract, VersionedArvoContract } from 'arvo-core';
|
|
2
|
+
import type { ArvoContract, ArvoEvent, ArvoOrchestratorContract, ArvoSemanticVersion, CreateArvoEvent, InferArvoEvent, InferVersionedArvoContract, VersionedArvoContract } from 'arvo-core';
|
|
3
3
|
import type { EnqueueArvoEventActionParam } from '../ArvoMachine/types';
|
|
4
4
|
import type { OrchestrationExecutionMemoryRecord } from '../ArvoOrchestrationUtils/orchestrationExecutionState';
|
|
5
|
+
import type { IMachineMemory } from '../MachineMemory/interface';
|
|
6
|
+
import type { ArvoEventHandlerOtelSpanOptions } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Extracts all possible event types (including system errors) from service contracts.
|
|
9
|
+
*/
|
|
5
10
|
type ExtractServiceEventTypes<TServiceContract extends Record<string, VersionedArvoContract<any, any>>> = {
|
|
6
11
|
[K in keyof TServiceContract]: {
|
|
7
12
|
[L in keyof InferVersionedArvoContract<TServiceContract[K]>['emits']]: {
|
|
@@ -13,142 +18,242 @@ type ExtractServiceEventTypes<TServiceContract extends Record<string, VersionedA
|
|
|
13
18
|
event: InferVersionedArvoContract<TServiceContract[K]>['systemError'];
|
|
14
19
|
};
|
|
15
20
|
}[keyof TServiceContract];
|
|
21
|
+
/**
|
|
22
|
+
* Union of all service event type strings.
|
|
23
|
+
*/
|
|
16
24
|
type AllServiceEventTypes<TServiceContract extends Record<string, VersionedArvoContract<any, any>>> = ExtractServiceEventTypes<TServiceContract>['type'];
|
|
25
|
+
/**
|
|
26
|
+
* Maps event type strings to their corresponding event schemas.
|
|
27
|
+
*/
|
|
17
28
|
type ServiceEventTypeMap<TServiceContract extends Record<string, VersionedArvoContract<any, any>>> = {
|
|
18
29
|
[T in ExtractServiceEventTypes<TServiceContract> as T['type']]: T['event'];
|
|
19
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Handler function signature for processing events in ArvoResumable workflows.
|
|
33
|
+
*
|
|
34
|
+
* Handlers are invoked for each incoming event (initialization or service response)
|
|
35
|
+
* and must be deterministic and idempotent to ensure reliable execution across retries.
|
|
36
|
+
*/
|
|
20
37
|
type Handler<TState extends ArvoResumableState<Record<string, any>>, TSelfContract extends VersionedArvoContract<any, any>, TServiceContract extends Record<string, VersionedArvoContract<any, any>>> = (param: {
|
|
38
|
+
/** OpenTelemetry span for distributed tracing and observability */
|
|
21
39
|
span: Span;
|
|
40
|
+
/**
|
|
41
|
+
* Complete workflow metadata including subject, parent info, and event history.
|
|
42
|
+
* Null for new workflow initialization.
|
|
43
|
+
*/
|
|
22
44
|
metadata: Omit<TState, 'state$$'> | null;
|
|
45
|
+
/**
|
|
46
|
+
* Map of collected service response events grouped by event type.
|
|
47
|
+
* Enables type-safe access to accumulated responses from external services.
|
|
48
|
+
* Events are collected based on matching parent IDs with emitted service calls.
|
|
49
|
+
*/
|
|
23
50
|
collectedEvents: Partial<{
|
|
24
51
|
[K in AllServiceEventTypes<TServiceContract>]: ServiceEventTypeMap<TServiceContract>[K][];
|
|
25
52
|
}>;
|
|
53
|
+
/** Domain information for routing events */
|
|
26
54
|
domain: {
|
|
55
|
+
/** Domain from the triggering event */
|
|
27
56
|
event: string | null;
|
|
57
|
+
/** Domain from the resumable's self contract */
|
|
28
58
|
self: string | null;
|
|
29
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* Current workflow state persisted from previous execution.
|
|
62
|
+
* Null for new workflows or when no state has been saved yet.
|
|
63
|
+
*/
|
|
30
64
|
context: TState['state$$'] | null;
|
|
65
|
+
/**
|
|
66
|
+
* Initialization event data for workflow start.
|
|
67
|
+
* Only present when handler is processing the initialization event that starts the workflow.
|
|
68
|
+
* Null for service response events.
|
|
69
|
+
*/
|
|
31
70
|
input: InferVersionedArvoContract<TSelfContract>['accepts'] | null;
|
|
71
|
+
/**
|
|
72
|
+
* Service response event data.
|
|
73
|
+
* Only present when handler is processing a response from an external service.
|
|
74
|
+
* Null for initialization events.
|
|
75
|
+
*/
|
|
32
76
|
service: {
|
|
33
77
|
[K in keyof TServiceContract]: {
|
|
34
78
|
[L in keyof InferVersionedArvoContract<TServiceContract[K]>['emits']]: InferVersionedArvoContract<TServiceContract[K]>['emits'][L];
|
|
35
79
|
}[keyof InferVersionedArvoContract<TServiceContract[K]>['emits']] | InferVersionedArvoContract<TServiceContract[K]>['systemError'];
|
|
36
80
|
}[keyof TServiceContract] | null;
|
|
81
|
+
/** Contract definitions available to the handler */
|
|
37
82
|
contracts: {
|
|
83
|
+
/** The resumable's self contract for validation */
|
|
38
84
|
self: TSelfContract;
|
|
85
|
+
/** Service contracts for emitting compliant events to external systems */
|
|
39
86
|
services: TServiceContract;
|
|
40
87
|
};
|
|
41
88
|
}) => Promise<{
|
|
89
|
+
/** Updated workflow state to persist for next invocation */
|
|
42
90
|
context?: TState['state$$'];
|
|
91
|
+
/**
|
|
92
|
+
* Workflow completion data.
|
|
93
|
+
* Returning this signals workflow completion and emits the final output event.
|
|
94
|
+
* The workflow status becomes 'done' and no further events are processed.
|
|
95
|
+
*/
|
|
43
96
|
output?: {
|
|
44
97
|
[L in keyof InferVersionedArvoContract<TSelfContract>['emits']]: EnqueueArvoEventActionParam<InferVersionedArvoContract<TSelfContract>['emits'][L]['data'], InferVersionedArvoContract<TSelfContract>['emits'][L]['type']>['data'];
|
|
45
98
|
}[keyof InferVersionedArvoContract<TSelfContract>['emits']] & {
|
|
46
99
|
__id?: CreateArvoEvent<Record<string, unknown>, string>['id'];
|
|
47
100
|
};
|
|
101
|
+
/**
|
|
102
|
+
* Service call events to emit.
|
|
103
|
+
* Each event triggers an external service and awaits its response in future invocations.
|
|
104
|
+
* Responses are collected in `collectedEvents` based on parent ID matching.
|
|
105
|
+
*/
|
|
48
106
|
services?: Array<{
|
|
49
107
|
[K in keyof TServiceContract]: EnqueueArvoEventActionParam<InferVersionedArvoContract<TServiceContract[K]>['accepts']['data'], InferVersionedArvoContract<TServiceContract[K]>['accepts']['type']>;
|
|
50
108
|
}[keyof TServiceContract]>;
|
|
51
109
|
} | void>;
|
|
52
110
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* It maps each version of an orchestrator contract to its corresponding handler function.
|
|
56
|
-
* Each handler receives workflow context (state, events, contracts) and returns execution results
|
|
57
|
-
* that can update state, complete the workflow, or invoke external services.
|
|
58
|
-
*
|
|
59
|
-
* The handler is called for each event that matches the orchestrator's contract, whether it's
|
|
60
|
-
* an initialization event or a service response. The handler must be deterministic and
|
|
61
|
-
* idempotent to ensure reliable workflow execution across potential retries.
|
|
62
|
-
*
|
|
63
|
-
* @param param - Handler execution context
|
|
64
|
-
* @param param.span - OpenTelemetry span for distributed tracing
|
|
65
|
-
* @param param.metadata - Complete workflow metadata (null for new workflows)
|
|
66
|
-
* @param param.collectedEvents - Type-safe map of event types to their corresponding typed event arrays,
|
|
67
|
-
* enabling strongly-typed access with full IntelliSense support.
|
|
68
|
-
* @param param.context - Current workflow state (null for new workflows)
|
|
69
|
-
* @param param.init - Initialization event data (only present for workflow start events)
|
|
70
|
-
* @param param.service - Service response event data (only present for service callbacks)
|
|
71
|
-
* @param param.contracts - Available contracts for type validation and event creation
|
|
72
|
-
* @param param.contracts.self - The orchestrator's own versioned contract
|
|
73
|
-
* @param param.contracts.services - External service contracts for invocation
|
|
111
|
+
* Versioned handler map for ArvoResumable workflows.
|
|
74
112
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @returns result.complete - Workflow completion event to emit (ends the workflow)
|
|
78
|
-
* @returns result.services - Array of service invocation events to emit
|
|
113
|
+
* Maps contract versions to their corresponding handler implementations.
|
|
114
|
+
* Each version can have different business logic while maintaining backward compatibility.
|
|
79
115
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* - State updates are atomic - either all changes persist or none do
|
|
84
|
-
* - Only one of `init` or `service` will be non-null for any given invocation
|
|
85
|
-
* - Returning void or an empty object indicates no state changes or events to emit
|
|
86
|
-
* - Service events are supposed to queued for execution and may trigger callback events
|
|
87
|
-
* - Completion events terminate the workflow and route to the parent orchestrator
|
|
116
|
+
* Handlers are invoked for initialization events and service responses matching the
|
|
117
|
+
* resumable's contract. They must be deterministic and idempotent to ensure reliable
|
|
118
|
+
* workflow execution across retries and failures.
|
|
88
119
|
*/
|
|
89
120
|
export type ArvoResumableHandler<TState extends ArvoResumableState<Record<string, any>>, TSelfContract extends ArvoContract, TServiceContract extends Record<string, VersionedArvoContract<any, any>>> = {
|
|
90
121
|
[V in ArvoSemanticVersion & keyof TSelfContract['versions']]: Handler<TState, VersionedArvoContract<TSelfContract, V>, TServiceContract>;
|
|
91
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* State structure persisted in memory for ArvoResumable workflows.
|
|
125
|
+
*
|
|
126
|
+
* Extends base orchestration state with resumable-specific fields including
|
|
127
|
+
* event collection, workflow status tracking, and custom state management.
|
|
128
|
+
*/
|
|
92
129
|
export type ArvoResumableState<T extends Record<string, any>> = OrchestrationExecutionMemoryRecord<{
|
|
93
130
|
/**
|
|
94
|
-
* Current
|
|
95
|
-
*
|
|
96
|
-
* This field tracks the lifecycle state of the workflow instance to determine
|
|
97
|
-
* whether it can accept new events and continue processing or has reached
|
|
98
|
-
* its terminal state.
|
|
131
|
+
* Current workflow status.
|
|
99
132
|
*
|
|
100
|
-
*
|
|
101
|
-
* -
|
|
102
|
-
*
|
|
103
|
-
* or handling intermediate workflow steps. The orchestrator will continue
|
|
104
|
-
* to route events to active workflows.
|
|
105
|
-
*
|
|
106
|
-
* - **done**: The workflow has completed its execution lifecycle. This status
|
|
107
|
-
* is set when the handler returns a `complete` event, indicating the workflow
|
|
108
|
-
* has finished successfully. Done workflows will not process additional events
|
|
109
|
-
* and their state is preserved for audit/debugging purposes.
|
|
133
|
+
* Determines whether the workflow can process additional events:
|
|
134
|
+
* - `'active'`: Workflow is running and accepts new events for processing
|
|
135
|
+
* - `'done'`: Workflow has completed (handler returned `output`). No further events are processed.
|
|
110
136
|
*/
|
|
111
137
|
status: 'active' | 'done';
|
|
112
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Unique identifier for the workflow instance.
|
|
140
|
+
* Serves as the key for state persistence in the memory store.
|
|
141
|
+
*/
|
|
113
142
|
subject: string;
|
|
114
143
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
144
|
+
* Parent orchestration subject for nested workflows.
|
|
145
|
+
*
|
|
146
|
+
* Enables hierarchical orchestration where one workflow spawns sub-workflows.
|
|
147
|
+
* Completion events route back to this parent subject.
|
|
119
148
|
*
|
|
120
|
-
* -
|
|
121
|
-
* -
|
|
122
|
-
* -
|
|
149
|
+
* - Root workflows: `null`
|
|
150
|
+
* - Nested workflows: parent's subject identifier
|
|
151
|
+
* - Source: `parentSubject$$` in initialization events
|
|
123
152
|
*/
|
|
124
153
|
parentSubject: string | null;
|
|
125
154
|
/**
|
|
126
|
-
*
|
|
127
|
-
* This serves as the root identifier for tracking the complete execution chain from start to finish.
|
|
155
|
+
* ID of the event that initiated this workflow.
|
|
128
156
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* - Used as the `parentid` for completion events to create a direct lineage back to the workflow's origin
|
|
157
|
+
* Root identifier for tracing the complete execution chain.
|
|
158
|
+
* Used as `parentid` for completion events to maintain lineage.
|
|
132
159
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
160
|
+
* - New workflows: current event's ID
|
|
161
|
+
* - Resumed workflows: retrieved from stored state
|
|
135
162
|
*/
|
|
136
163
|
initEventId: string;
|
|
164
|
+
/**
|
|
165
|
+
* Event history for the current invocation.
|
|
166
|
+
* Transient collection tracking events consumed, produced, and expected.
|
|
167
|
+
*/
|
|
137
168
|
events: {
|
|
138
|
-
/**
|
|
169
|
+
/** Event consumed in the last handler invocation */
|
|
139
170
|
consumed: InferArvoEvent<ArvoEvent> | null;
|
|
140
|
-
/**
|
|
141
|
-
* The domained events produced by the machine in the last session
|
|
142
|
-
*/
|
|
171
|
+
/** Events produced (with domain resolution) in the last invocation */
|
|
143
172
|
produced: InferArvoEvent<ArvoEvent>[];
|
|
144
173
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
174
|
+
* Service response events awaiting collection.
|
|
175
|
+
*
|
|
176
|
+
* Keyed by the emitted event's ID (parent ID of responses).
|
|
177
|
+
* Responses are collected when their `parentid` matches a produced event's `id`.
|
|
178
|
+
* Collected events are passed to the handler via `collectedEvents` parameter.
|
|
148
179
|
*/
|
|
149
180
|
expected: Record<string, InferArvoEvent<ArvoEvent>[]> | null;
|
|
150
181
|
};
|
|
151
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* Custom workflow state managed by the handler.
|
|
184
|
+
* Accessible via the `context` parameter in handlers and persisted between invocations.
|
|
185
|
+
*/
|
|
152
186
|
state$$: T | null;
|
|
153
187
|
}>;
|
|
188
|
+
/**
|
|
189
|
+
* Configuration parameters for creating an ArvoResumable instance.
|
|
190
|
+
*
|
|
191
|
+
* Defines all required components for a resumable workflow orchestrator including
|
|
192
|
+
* contracts, handlers, memory, and execution settings.
|
|
193
|
+
*/
|
|
194
|
+
export type ArvoResumableParam<TMemory extends Record<string, any>, TSelfContract extends ArvoOrchestratorContract, TServiceContract extends Record<string, VersionedArvoContract<any, any>>> = {
|
|
195
|
+
/**
|
|
196
|
+
* Contract definitions for the resumable's event interface.
|
|
197
|
+
* Defines accepted events, emitted events, and service integrations.
|
|
198
|
+
*/
|
|
199
|
+
contracts: {
|
|
200
|
+
/**
|
|
201
|
+
* Self contract defining initialization input and completion output structures.
|
|
202
|
+
*/
|
|
203
|
+
self: TSelfContract;
|
|
204
|
+
/**
|
|
205
|
+
* Service contracts defining external service interfaces.
|
|
206
|
+
* Enables type-safe event emission and response handling for external systems.
|
|
207
|
+
*/
|
|
208
|
+
services: TServiceContract;
|
|
209
|
+
};
|
|
210
|
+
/** Computational cost metric for workflow operations */
|
|
211
|
+
executionunits: number;
|
|
212
|
+
/** Memory interface for state persistence and retrieval */
|
|
213
|
+
memory: IMachineMemory<ArvoResumableState<TMemory>>;
|
|
214
|
+
/** Whether to enforce resource locking for concurrent execution safety */
|
|
215
|
+
requiresResourceLocking: boolean;
|
|
216
|
+
/**
|
|
217
|
+
* Versioned handler map for processing workflow events.
|
|
218
|
+
* Each contract version maps to its corresponding handler implementation.
|
|
219
|
+
*/
|
|
220
|
+
handler: ArvoResumableHandler<ArvoResumableState<TMemory>, TSelfContract, TServiceContract>;
|
|
221
|
+
/** Optional domains for system error event routing */
|
|
222
|
+
systemErrorDomain?: (string | null)[];
|
|
223
|
+
/** OpenTelemetry span configuration for distributed tracing */
|
|
224
|
+
spanOptions?: ArvoEventHandlerOtelSpanOptions;
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Configuration parameters for creating an ArvoResumable instance.
|
|
228
|
+
*/
|
|
229
|
+
export type CreateArvoResumableParam<TMemory extends Record<string, any>, TSelfContract extends ArvoOrchestratorContract, TServiceContract extends Record<string, VersionedArvoContract<any, any>> = Record<string, VersionedArvoContract<any, any>>> = {
|
|
230
|
+
/** Optional type hints for TypeScript inference (not used at runtime) */
|
|
231
|
+
types?: {
|
|
232
|
+
context?: Partial<TMemory>;
|
|
233
|
+
};
|
|
234
|
+
/** Contract definitions for event interface validation */
|
|
235
|
+
contracts: {
|
|
236
|
+
/** Self contract defining initialization and completion events */
|
|
237
|
+
self: TSelfContract;
|
|
238
|
+
/** Service contracts for external system integrations */
|
|
239
|
+
services: TServiceContract;
|
|
240
|
+
};
|
|
241
|
+
/** Memory interface for state persistence */
|
|
242
|
+
memory: IMachineMemory<Record<string, any>>;
|
|
243
|
+
/** Versioned handler map for processing events */
|
|
244
|
+
handler: ArvoResumableHandler<ArvoResumableState<TMemory>, TSelfContract, TServiceContract>;
|
|
245
|
+
/**
|
|
246
|
+
* Computational cost metric for operations.
|
|
247
|
+
*/
|
|
248
|
+
executionunits?: number;
|
|
249
|
+
/**
|
|
250
|
+
* Whether to enforce resource locking for concurrent safety.
|
|
251
|
+
* @default true if multiple service contracts, false otherwise
|
|
252
|
+
*/
|
|
253
|
+
requiresResourceLocking?: boolean;
|
|
254
|
+
/** Optional domains for system error event routing */
|
|
255
|
+
systemErrorDomain?: (string | null)[];
|
|
256
|
+
/** OpenTelemetry span configuration for distributed tracing */
|
|
257
|
+
spanOptions?: ArvoEventHandlerOtelSpanOptions;
|
|
258
|
+
};
|
|
154
259
|
export {};
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
import type { ArvoContractRecord, ArvoEvent } from 'arvo-core';
|
|
2
2
|
import type { ArvoEventHandlerOpenTelemetryOptions } from '../types';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Interface for Arvo event handlers.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Defines the contract for all event handlers in the Arvo system, including
|
|
7
|
+
* orchestrators, resumable handlers, and custom handlers. Each handler must
|
|
8
|
+
* implement event execution logic and provide system error schema configuration.
|
|
9
9
|
*/
|
|
10
10
|
export default interface IArvoEventHandler {
|
|
11
11
|
/**
|
|
12
|
-
* Unique identifier for the event handler source system
|
|
12
|
+
* Unique identifier for the event handler's source system.
|
|
13
|
+
* Used for event routing and tracing throughout the system.
|
|
13
14
|
*/
|
|
14
15
|
source: string;
|
|
15
16
|
/**
|
|
16
|
-
* Executes the event handling logic for
|
|
17
|
+
* Executes the event handling logic for an incoming Arvo event.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* Processes the event according to the handler's business logic and returns
|
|
20
|
+
* resulting events to be emitted. The handler may emit multiple events as
|
|
21
|
+
* outcomes of processing a single input event.
|
|
20
22
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* For violation errors (transaction, execution, contract, config), implementations
|
|
24
|
+
* typically throw the error to enable retry mechanisms. For non-violation errors,
|
|
25
|
+
* implementations typically emit system error events to the workflow initiator.
|
|
26
|
+
*
|
|
27
|
+
* @param event - The Arvo event to be processed
|
|
28
|
+
* @param opentelemetry - Optional configuration for distributed tracing integration
|
|
29
|
+
* @returns Promise resolving to an object containing emitted events
|
|
30
|
+
*
|
|
31
|
+
* @throws {ViolationError} When retriable errors occur (lock failures, validation errors, etc.)
|
|
23
32
|
*/
|
|
24
33
|
execute(event: ArvoEvent, opentelemetry?: ArvoEventHandlerOpenTelemetryOptions): Promise<{
|
|
25
34
|
events: ArvoEvent[];
|
|
26
35
|
}>;
|
|
27
36
|
/**
|
|
28
|
-
*
|
|
37
|
+
* Schema configuration for system error events.
|
|
29
38
|
*
|
|
30
|
-
*
|
|
39
|
+
* Defines the structure and routing for error events emitted when unexpected
|
|
40
|
+
* errors occur during event handling. System errors are sent to the workflow
|
|
41
|
+
* initiator to signal terminal failures that cannot be automatically recovered.
|
|
31
42
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
43
|
+
* @property type - The error event type identifier
|
|
44
|
+
* @property schema - Zod schema defining the error event data structure
|
|
45
|
+
* @property domain - Optional domains for error event routing and distribution
|
|
34
46
|
*/
|
|
35
|
-
systemErrorSchema: ArvoContractRecord
|
|
47
|
+
systemErrorSchema: ArvoContractRecord & {
|
|
48
|
+
domain?: (string | null)[];
|
|
49
|
+
};
|
|
36
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arvo-event-handler",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.16",
|
|
4
4
|
"description": "Type-safe event handler system with versioning, telemetry, and contract validation for distributed Arvo event-driven architectures, featuring routing and multi-handler support.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@opentelemetry/api": "^1.9.0",
|
|
48
48
|
"@opentelemetry/core": "^1.30.1",
|
|
49
|
-
"arvo-core": "^3.0.
|
|
49
|
+
"arvo-core": "^3.0.16",
|
|
50
50
|
"uuid": "^11.1.0",
|
|
51
51
|
"xstate": "^5.23.0",
|
|
52
52
|
"zod": "^3.25.74",
|