arvo-event-handler 3.0.2 → 3.0.4
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/README.md +65 -65
- package/dist/ArvoDomain.d.ts +57 -0
- package/dist/ArvoDomain.js +71 -0
- package/dist/ArvoEventHandler/helpers.d.ts +15 -26
- package/dist/ArvoEventHandler/helpers.js +15 -26
- package/dist/ArvoEventHandler/index.d.ts +64 -55
- package/dist/ArvoEventHandler/index.js +93 -67
- package/dist/ArvoEventHandler/types.d.ts +34 -12
- package/dist/ArvoMachine/types.d.ts +20 -12
- package/dist/ArvoOrchestrator/factory.d.ts +3 -2
- package/dist/ArvoOrchestrator/factory.js +4 -2
- package/dist/ArvoOrchestrator/index.d.ts +3 -8
- package/dist/ArvoOrchestrator/index.js +55 -27
- package/dist/ArvoOrchestrator/types.d.ts +18 -0
- package/dist/ArvoResumable/factory.d.ts +2 -10
- package/dist/ArvoResumable/factory.js +2 -10
- package/dist/ArvoResumable/index.d.ts +3 -34
- package/dist/ArvoResumable/index.js +60 -56
- package/dist/ArvoResumable/types.d.ts +4 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,101 +1,102 @@
|
|
|
1
|
+
# Arvo Event Handler
|
|
2
|
+
|
|
3
|
+
The `arvo-event-handler` package serves as the comprehensive orchestration and event processing foundation for building sophisticated, reliable event-driven systems within the Arvo architecture. This package provides a complete toolkit of components that work seamlessly together to handle everything from simple event processing to complex distributed workflow orchestration, all while maintaining strict type safety, comprehensive observability, and robust error handling.
|
|
4
|
+
|
|
1
5
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
2
6
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
## Installation
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
Install the package along with its core dependency:
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
```bash
|
|
13
|
+
npm install arvo-event-handler arvo-core
|
|
14
|
+
```
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
```bash
|
|
17
|
+
yarn add arvo-event-handler arvo-core
|
|
18
|
+
```
|
|
11
19
|
|
|
12
|
-
The
|
|
20
|
+
## The Event Handlers
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
The Arvo event handling architecture is based on three handler patterns.
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
### 1. Simple Event Handler
|
|
17
25
|
|
|
18
|
-
-
|
|
19
|
-
- Extensible architecture
|
|
20
|
-
- Cloud-agnostic design
|
|
21
|
-
- Built-in primitives for event-driven patterns
|
|
22
|
-
- Easy integration with existing systems and tools
|
|
26
|
+
This kind of event handling is provided by [`ArvoEventHandler`](src/ArvoEventHandler/README.md). This approach transforms ArvoContract definitions into stateless, pure function handlers that process individual events in isolation. Each handler binds to a specific contract, validates incoming events against schema definitions, executes business logic, and returns response events. It supports multiple contract versions for backward compatibility and enables multi-domain event broadcasting for parallel processing pipelines. This pattern is ideal for microservices, API endpoints, and any scenario where you need reliable, contract-enforced event processing without complex state management or workflow coordination.
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
### 2. State-machine based workflow orchestration
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
This kind of event handling is provided by [`ArvoMachine`](src/ArvoMachine/README.md) which defines the state machine and [`ArvoOrchestrator`](src/ArvoOrchestrator/README.md) which executes it. This approach uses declarative state machine definitions to model complex business processes with multiple states, transitions, and conditional logic. ArvoMachine creates XState-compatible machines with Arvo-specific constraints and contract bindings, while ArvoOrchestrator provides the runtime environment for executing these machines with distributed state persistence, resource locking, and comprehensive lifecycle management. This pattern excels at complex workflows with parallel states, timing requirements, conditional branching, and scenarios where visual workflow modeling and deterministic state transitions are crucial for business process management.
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
### 3. Dynamic stateful event handling and orchestration
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
| -------------- | ----------------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------ |
|
|
32
|
-
| Orchestration | https://www.npmjs.com/package/arvo-xstate?activeTab=readme | https://github.com/SaadAhmad123/arvo-xstate | https://saadahmad123.github.io/arvo-xstate/index.html |
|
|
33
|
-
| Core | https://www.npmjs.com/package/arvo-core?activeTab=readme | https://github.com/SaadAhmad123/arvo-core | https://saadahmad123.github.io/arvo-core/index.html |
|
|
34
|
-
| Event Handling | https://www.npmjs.com/package/arvo-event-handler?activeTab=readme | https://github.com/SaadAhmad123/arvo-event-handler | https://saadahmad123.github.io/arvo-event-handler/index.html |
|
|
34
|
+
This kind of event handling is provided by [`ArvoResumable`](src/ArvoResumable/README.md). The event handling is a different approach to workflow processing and complements the state machine pattern by offering an imperative programming model where developers write handler functions that explicitly manage workflow state through context objects. Instead of defining states and transitions declaratively, you write code that examines incoming events, updates workflow context, and decides what actions to take next. This approach provides direct control over workflow logic, making it easier to debug and understand for teams familiar with traditional programming patterns, while still offering the same reliability, observability, and distributed coordination features as state machine orchestration.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
## Core Infrastructure Components
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Beyond the three main handler patterns, the package includes essential infrastructure components that enable robust distributed system operation.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
### Memory - State Persistance
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
| ------------ | ----------------------------------------------------------------- |
|
|
44
|
-
| Package | https://www.npmjs.com/package/arvo-event-handler?activeTab=readme |
|
|
45
|
-
| Github | https://github.com/SaadAhmad123/arvo-event-handler |
|
|
46
|
-
| Documenation | https://saadahmad123.github.io/arvo-event-handler/index.html |
|
|
42
|
+
The [`IMachineMemory`](src/MachineMemory/README.md) interface defines how workflow state gets persisted and coordinated across distributed instances. It implements an optimistic locking strategy with "fail fast on acquire, be tolerant on release" semantics, ensuring data consistency while enabling system recovery from transient failures.
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
This package includes `SimpleMachineMemory` for development/ prototyping scenarios and provides example for implementing cloud-based production-ready distributed storage solutions.
|
|
49
45
|
|
|
50
|
-
|
|
46
|
+
### Error Handling
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
npm install arvo-event-handler arvo-core
|
|
54
|
-
```
|
|
48
|
+
The Arvo event handling system uses a layered error handling approach that provides clear boundaries between different types of failures, enabling appropriate responses at each level.
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
yarn add arvo-event-handler arvo-core
|
|
58
|
-
```
|
|
50
|
+
**Business Logic Failures** are expected outcomes in your business processes and should be modeled as explicit events in your `ArvoContract` definitions. For example, when a user already exists during registration or a payment is declined, these represent normal business scenarios rather than system errors. By defining these as emittable events, downstream consumers can distinguish between business logic outcomes and actual system problems, enabling appropriate handling logic for each scenario.
|
|
59
51
|
|
|
60
|
-
|
|
52
|
+
**Transient System Errors** occur when underlying infrastructure or external services fail temporarily. Database connection timeouts, API unavailability, or network issues fall into this category. The system automatically converts uncaught exceptions into standardized system error events with the type pattern `sys.{contract.type}.error`. These events carry error details and can trigger retry mechanisms, circuit breakers, or alternative processing paths while maintaining the event-driven flow of your system.
|
|
61
53
|
|
|
62
|
-
|
|
54
|
+
**Violations** represent critical failures that require immediate attention and cannot be handled through normal event processing patterns. The system defines four distinct violation types to help you identify and respond to different categories of critical issues:
|
|
63
55
|
|
|
64
|
-
-
|
|
65
|
-
- [ArvoEventRouter](src/ArvoEventRouter/README.md) is designed to route ArvoEvents to appropriate ArvoEventHandlers. It provides a centralized mechanism for managing and executing multiple event handlers based on event types.
|
|
66
|
-
- [MultiArvoEventHandler](src/MultiArvoEventHandler/README.md) is a flexible and powerful event handling class designed to process multiple event types across different ArvoContracts. This handler offers greater versatility compared to the more specialized `ArvoEventHandler`, as it's not bound to a specific contract or event type.
|
|
56
|
+
- `ContractViolation` occurs when event data fails contract validation, indicating schema mismatches between services. This typically signals version incompatibilities or data corruption that requires developer intervention to resolve.
|
|
67
57
|
|
|
68
|
-
|
|
58
|
+
- `ConfigViolation` happens when events are routed to handlers that cannot process them, revealing system topology or configuration problems that need infrastructure-level fixes.
|
|
69
59
|
|
|
70
|
-
|
|
60
|
+
- `ExecutionViolation` provides a mechanism for custom error handling when your business logic encounters scenarios that cannot be resolved through normal event patterns and require special intervention.
|
|
71
61
|
|
|
72
|
-
-
|
|
73
|
-
- Import the necessary components:
|
|
62
|
+
- `TransactionViolation` is raised specifically by `ArvoOrchestrator` and `ArvoResumable` when state persistence operations fail. The accompanying `TransactionViolationCause` provides detailed information about what went wrong, allowing you to implement appropriate recovery strategies for distributed transaction failures.
|
|
74
63
|
|
|
75
|
-
```javascript
|
|
76
|
-
import {
|
|
77
|
-
createArvoEvent,
|
|
78
|
-
createArvoContract,
|
|
79
|
-
createArvoContractLibrary,
|
|
80
|
-
createArvoEventFactory,
|
|
81
|
-
} from 'arvo-core';
|
|
82
64
|
|
|
83
|
-
|
|
84
|
-
createArvoEventHandler,
|
|
85
|
-
createMultiArvoEventHandler,
|
|
86
|
-
createArvoEventRouter,
|
|
87
|
-
} from 'arvo-event-handler';
|
|
88
|
-
```
|
|
65
|
+
### Local Developement & Testing
|
|
89
66
|
|
|
90
|
-
|
|
67
|
+
The package provides `createSimpleEventBroker` utility which creates local event buses perfect for testing, development, and single-function workflow coordination. It enables comprehensive integration testing without external message brokers while supporting the same event patterns used in production distributed systems.
|
|
91
68
|
|
|
92
|
-
## License
|
|
93
69
|
|
|
94
|
-
|
|
70
|
+
## Architecture Principles
|
|
71
|
+
|
|
72
|
+
The entire system follows consistent architectural principles that promote reliability and maintainability. All handlers implement the signature `ArvoEvent => Promise<{ events: ArvoEvent[] }>`, creating predictable event flow patterns throughout the system. Contract-first development ensures all service interactions are explicitly defined and validated, eliminating common integration issues while providing compile-time type safety.
|
|
73
|
+
|
|
74
|
+
Multi-domain event broadcasting allows single handlers to create events for different processing contexts simultaneously, supporting patterns like audit trails, analytics processing, and external system integration. The comprehensive observability integration provides operational visibility through OpenTelemetry spans, structured logging, and performance metrics collection.
|
|
75
|
+
|
|
76
|
+
The functional architecture enables natural horizontal scaling since handlers operate as pure functions with consistent behavior regardless of deployment location. State management through pluggable persistence interfaces supports various scaling strategies from single-instance deployments to sophisticated distributed configurations.
|
|
95
77
|
|
|
96
|
-
##
|
|
78
|
+
## Documentation and Resources
|
|
97
79
|
|
|
98
|
-
|
|
80
|
+
| Component | Documentation | When to Use |
|
|
81
|
+
|-----------|---------------|-------------|
|
|
82
|
+
| **ArvoEventHandler** | [Simple Event Processing](src/ArvoEventHandler/README.md) | Stateless services, API endpoints, microservices, simple request-response processing |
|
|
83
|
+
| **ArvoMachine** | [State Machine Workflows](src/ArvoMachine/README.md) | Complex business processes with multiple states, conditional branching, parallel execution, visual workflow modeling |
|
|
84
|
+
| **ArvoOrchestrator** | [Workflow Orchestration](src/ArvoOrchestrator/README.md) | Running state machines in production, distributed workflow coordination, comprehensive lifecycle management |
|
|
85
|
+
| **ArvoResumable** | [Handler-Based Workflows](src/ArvoResumable/README.md) | Dynamic workflows, imperative programming preference, rapid prototyping, teams familiar with traditional programming patterns |
|
|
86
|
+
| **MachineMemory** | [State Persistence Interface](src/MachineMemory/README.md) | Custom state storage requirements, distributed locking strategies, production persistence implementations |
|
|
87
|
+
|
|
88
|
+
## Package Information
|
|
89
|
+
|
|
90
|
+
| Resource | Link |
|
|
91
|
+
|----------|------|
|
|
92
|
+
| Package | [npm package](https://www.npmjs.com/package/arvo-event-handler) |
|
|
93
|
+
| Repository | [GitHub repository](https://github.com/SaadAhmad123/arvo-event-handler) |
|
|
94
|
+
| Documentation | [Complete documentation](https://saadahmad123.github.io/arvo-event-handler/index.html) |
|
|
95
|
+
| Core Package | [arvo-core documentation](https://saadahmad123.github.io/arvo-core/index.html) |
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
This package is available under the MIT License. For more details, refer to the [LICENSE.md](LICENSE.md) file in the project repository.
|
|
99
100
|
|
|
100
101
|
### SonarCloud Metrics
|
|
101
102
|
|
|
@@ -106,8 +107,7 @@ For a detailed list of changes and updates, please refer to the [document](CHANG
|
|
|
106
107
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
107
108
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
108
109
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
109
|
-
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
110
110
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
111
111
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
112
112
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
113
|
-
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
113
|
+
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ArvoEvent, VersionedArvoContract } from 'arvo-core';
|
|
2
|
+
/**
|
|
3
|
+
* Symbolic constants for domain resolution in Arvo event emission.
|
|
4
|
+
*
|
|
5
|
+
* These can be passed into the `domain` array of an emitted ArvoEvent to indicate
|
|
6
|
+
* that the final domain should be dynamically resolved from a specific context.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ArvoDomain: {
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the domain from the emitting handler’s own contract (`handlerSelfContract.domain`).
|
|
11
|
+
*
|
|
12
|
+
* Use this when the handler’s contract defines a stable domain that should apply
|
|
13
|
+
* to all emitted events, regardless of the triggering context.
|
|
14
|
+
*/
|
|
15
|
+
readonly FROM_SELF_CONTRACT: "domain.contract.self.inherit";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the domain from the contract that defines the event being emitted (`eventContract.domain`).
|
|
18
|
+
*
|
|
19
|
+
* In `ArvoResumable` and `ArvoMachine`, this is typically used when emitting service events
|
|
20
|
+
* (not the completion event). In `ArvoEventHandler`, where only the self contract exists,
|
|
21
|
+
* this resolves to the same value as `FROM_SELF_CONTRACT`.
|
|
22
|
+
*
|
|
23
|
+
* For orchestration `complete` events, this behaves identically to `FROM_SELF_CONTRACT`
|
|
24
|
+
* since the emitting contract is also the self contract.
|
|
25
|
+
*/
|
|
26
|
+
readonly FROM_EVENT_CONTRACT: "domain.contract.inherit";
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the domain from the triggering event’s `domain` field (`triggeringEvent.domain`).
|
|
29
|
+
*
|
|
30
|
+
* Use this when you want to preserve the domain context of the incoming event
|
|
31
|
+
* and carry it forward through event emissions.
|
|
32
|
+
*/
|
|
33
|
+
readonly FROM_TRIGGERING_EVENT: "domain.event.inherit";
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Resolves a symbolic or static domain value into a concrete domain string or `null`.
|
|
37
|
+
*
|
|
38
|
+
* Used internally in the Arvo execution model to interpret symbolic domain constants
|
|
39
|
+
* at the moment an event is emitted. Supports resolution from:
|
|
40
|
+
* - the emitting handler's own contract
|
|
41
|
+
* - the emitted event’s associated contract
|
|
42
|
+
* - the triggering event’s `domain` field
|
|
43
|
+
*
|
|
44
|
+
* @param param - Parameters for resolving the domain.
|
|
45
|
+
* @param param.domainToResolve - Either a static domain string, symbolic value, or null.
|
|
46
|
+
* @param param.handlerSelfContract - The contract of the handler currently emitting the event.
|
|
47
|
+
* @param param.eventContract - The contract of the event being emitted (optional).
|
|
48
|
+
* @param param.triggeringEvent - The triggering event that caused this emission.
|
|
49
|
+
*
|
|
50
|
+
* @returns A resolved domain string, or `null` if no valid domain is found.
|
|
51
|
+
*/
|
|
52
|
+
export declare const resolveEventDomain: (param: {
|
|
53
|
+
domainToResolve: string | null;
|
|
54
|
+
handlerSelfContract: VersionedArvoContract<any, any>;
|
|
55
|
+
eventContract: VersionedArvoContract<any, any> | null;
|
|
56
|
+
triggeringEvent: ArvoEvent;
|
|
57
|
+
}) => string | null;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveEventDomain = exports.ArvoDomain = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Symbolic constants for domain resolution in Arvo event emission.
|
|
6
|
+
*
|
|
7
|
+
* These can be passed into the `domain` array of an emitted ArvoEvent to indicate
|
|
8
|
+
* that the final domain should be dynamically resolved from a specific context.
|
|
9
|
+
*/
|
|
10
|
+
exports.ArvoDomain = {
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the domain from the emitting handler’s own contract (`handlerSelfContract.domain`).
|
|
13
|
+
*
|
|
14
|
+
* Use this when the handler’s contract defines a stable domain that should apply
|
|
15
|
+
* to all emitted events, regardless of the triggering context.
|
|
16
|
+
*/
|
|
17
|
+
FROM_SELF_CONTRACT: 'domain.contract.self.inherit',
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the domain from the contract that defines the event being emitted (`eventContract.domain`).
|
|
20
|
+
*
|
|
21
|
+
* In `ArvoResumable` and `ArvoMachine`, this is typically used when emitting service events
|
|
22
|
+
* (not the completion event). In `ArvoEventHandler`, where only the self contract exists,
|
|
23
|
+
* this resolves to the same value as `FROM_SELF_CONTRACT`.
|
|
24
|
+
*
|
|
25
|
+
* For orchestration `complete` events, this behaves identically to `FROM_SELF_CONTRACT`
|
|
26
|
+
* since the emitting contract is also the self contract.
|
|
27
|
+
*/
|
|
28
|
+
FROM_EVENT_CONTRACT: 'domain.contract.inherit',
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the domain from the triggering event’s `domain` field (`triggeringEvent.domain`).
|
|
31
|
+
*
|
|
32
|
+
* Use this when you want to preserve the domain context of the incoming event
|
|
33
|
+
* and carry it forward through event emissions.
|
|
34
|
+
*/
|
|
35
|
+
FROM_TRIGGERING_EVENT: 'domain.event.inherit',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Resolves a symbolic or static domain value into a concrete domain string or `null`.
|
|
39
|
+
*
|
|
40
|
+
* Used internally in the Arvo execution model to interpret symbolic domain constants
|
|
41
|
+
* at the moment an event is emitted. Supports resolution from:
|
|
42
|
+
* - the emitting handler's own contract
|
|
43
|
+
* - the emitted event’s associated contract
|
|
44
|
+
* - the triggering event’s `domain` field
|
|
45
|
+
*
|
|
46
|
+
* @param param - Parameters for resolving the domain.
|
|
47
|
+
* @param param.domainToResolve - Either a static domain string, symbolic value, or null.
|
|
48
|
+
* @param param.handlerSelfContract - The contract of the handler currently emitting the event.
|
|
49
|
+
* @param param.eventContract - The contract of the event being emitted (optional).
|
|
50
|
+
* @param param.triggeringEvent - The triggering event that caused this emission.
|
|
51
|
+
*
|
|
52
|
+
* @returns A resolved domain string, or `null` if no valid domain is found.
|
|
53
|
+
*/
|
|
54
|
+
var resolveEventDomain = function (param) {
|
|
55
|
+
var _a, _b;
|
|
56
|
+
var ArvoDomainValues = Object.values(exports.ArvoDomain);
|
|
57
|
+
if (param.domainToResolve && ArvoDomainValues.includes(param.domainToResolve)) {
|
|
58
|
+
var domainToResolve = param.domainToResolve;
|
|
59
|
+
if (domainToResolve === exports.ArvoDomain.FROM_EVENT_CONTRACT) {
|
|
60
|
+
return (_b = (_a = param.eventContract) === null || _a === void 0 ? void 0 : _a.domain) !== null && _b !== void 0 ? _b : null;
|
|
61
|
+
}
|
|
62
|
+
if (domainToResolve === exports.ArvoDomain.FROM_SELF_CONTRACT) {
|
|
63
|
+
return param.handlerSelfContract.domain;
|
|
64
|
+
}
|
|
65
|
+
if (domainToResolve === exports.ArvoDomain.FROM_TRIGGERING_EVENT) {
|
|
66
|
+
return param.triggeringEvent.domain;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return param.domainToResolve;
|
|
70
|
+
};
|
|
71
|
+
exports.resolveEventDomain = resolveEventDomain;
|
|
@@ -2,55 +2,44 @@ import type { ArvoContract } from 'arvo-core';
|
|
|
2
2
|
import ArvoEventHandler from '.';
|
|
3
3
|
import type { IArvoEventHandler } from './types';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Creates an instance of `ArvoEventHandler` for the specified versioned contract and handlers.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* This function is the recommended entry point for defining stateless, contract-driven services in Arvo.
|
|
8
|
+
* It binds a contract to its versioned handler implementations, enforces type-safe validation using Zod,
|
|
9
|
+
* and supports multi-domain event broadcasting and OpenTelemetry observability out of the box.
|
|
10
10
|
*
|
|
11
|
-
* See {@link ArvoEventHandler}
|
|
11
|
+
* See {@link ArvoEventHandler} for implementation details.
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
|
-
* ```
|
|
14
|
+
* ```ts
|
|
15
15
|
* const handler = createArvoEventHandler({
|
|
16
16
|
* contract: userContract,
|
|
17
17
|
* executionunits: 1,
|
|
18
18
|
* handler: {
|
|
19
19
|
* '1.0.0': async ({ event, domain, span }) => {
|
|
20
|
-
* // Access domain context
|
|
21
|
-
* if (event.domain === 'priority.high') {
|
|
22
|
-
* // Handle high-priority processing
|
|
23
|
-
* }
|
|
24
|
-
*
|
|
25
20
|
* if (domain.event !== domain.self) {
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
21
|
+
* logToSpan({
|
|
22
|
+
* level: 'WARN',
|
|
23
|
+
* message: 'Domain mismatch detected'
|
|
24
|
+
* }, span);
|
|
30
25
|
* }
|
|
31
26
|
*
|
|
32
|
-
*
|
|
33
|
-
* // Process event according to contract v1.0.0
|
|
34
27
|
* const result = await processUser(event.data);
|
|
35
28
|
*
|
|
36
|
-
* // Return structured response
|
|
37
29
|
* return {
|
|
38
30
|
* type: 'evt.user.created',
|
|
39
31
|
* data: result,
|
|
40
|
-
* // Optional: override default routing
|
|
41
32
|
* to: 'com.notification.service',
|
|
42
|
-
* // Creates 2 events:
|
|
43
|
-
* // - One for 'analytics.realtime' domain (specialized processing)
|
|
44
|
-
* // - One with no domain (standard processing pipeline)
|
|
45
|
-
* domain: ['analytics.realtime', null]
|
|
46
33
|
* };
|
|
47
34
|
* },
|
|
48
|
-
* '2.0.0': async ({ event, contract
|
|
49
|
-
* //
|
|
50
|
-
* // Handler must be implemented for all contract versions
|
|
35
|
+
* '2.0.0': async ({ event, contract }) => {
|
|
36
|
+
* // Handler logic for v2.0.0
|
|
51
37
|
* }
|
|
52
38
|
* }
|
|
53
39
|
* });
|
|
54
40
|
* ```
|
|
41
|
+
*
|
|
42
|
+
* @param param - Configuration object containing contract, versioned handlers, execution units, and span settings
|
|
43
|
+
* @returns A fully configured `ArvoEventHandler` instance for the given contract
|
|
55
44
|
*/
|
|
56
45
|
export declare const createArvoEventHandler: <TContract extends ArvoContract>(param: IArvoEventHandler<TContract>) => ArvoEventHandler<TContract>;
|
|
@@ -6,56 +6,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.createArvoEventHandler = void 0;
|
|
7
7
|
var _1 = __importDefault(require("."));
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Creates an instance of `ArvoEventHandler` for the specified versioned contract and handlers.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* This function is the recommended entry point for defining stateless, contract-driven services in Arvo.
|
|
12
|
+
* It binds a contract to its versioned handler implementations, enforces type-safe validation using Zod,
|
|
13
|
+
* and supports multi-domain event broadcasting and OpenTelemetry observability out of the box.
|
|
14
14
|
*
|
|
15
|
-
* See {@link ArvoEventHandler}
|
|
15
|
+
* See {@link ArvoEventHandler} for implementation details.
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
|
-
* ```
|
|
18
|
+
* ```ts
|
|
19
19
|
* const handler = createArvoEventHandler({
|
|
20
20
|
* contract: userContract,
|
|
21
21
|
* executionunits: 1,
|
|
22
22
|
* handler: {
|
|
23
23
|
* '1.0.0': async ({ event, domain, span }) => {
|
|
24
|
-
* // Access domain context
|
|
25
|
-
* if (event.domain === 'priority.high') {
|
|
26
|
-
* // Handle high-priority processing
|
|
27
|
-
* }
|
|
28
|
-
*
|
|
29
24
|
* if (domain.event !== domain.self) {
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
25
|
+
* logToSpan({
|
|
26
|
+
* level: 'WARN',
|
|
27
|
+
* message: 'Domain mismatch detected'
|
|
28
|
+
* }, span);
|
|
34
29
|
* }
|
|
35
30
|
*
|
|
36
|
-
*
|
|
37
|
-
* // Process event according to contract v1.0.0
|
|
38
31
|
* const result = await processUser(event.data);
|
|
39
32
|
*
|
|
40
|
-
* // Return structured response
|
|
41
33
|
* return {
|
|
42
34
|
* type: 'evt.user.created',
|
|
43
35
|
* data: result,
|
|
44
|
-
* // Optional: override default routing
|
|
45
36
|
* to: 'com.notification.service',
|
|
46
|
-
* // Creates 2 events:
|
|
47
|
-
* // - One for 'analytics.realtime' domain (specialized processing)
|
|
48
|
-
* // - One with no domain (standard processing pipeline)
|
|
49
|
-
* domain: ['analytics.realtime', null]
|
|
50
37
|
* };
|
|
51
38
|
* },
|
|
52
|
-
* '2.0.0': async ({ event, contract
|
|
53
|
-
* //
|
|
54
|
-
* // Handler must be implemented for all contract versions
|
|
39
|
+
* '2.0.0': async ({ event, contract }) => {
|
|
40
|
+
* // Handler logic for v2.0.0
|
|
55
41
|
* }
|
|
56
42
|
* }
|
|
57
43
|
* });
|
|
58
44
|
* ```
|
|
45
|
+
*
|
|
46
|
+
* @param param - Configuration object containing contract, versioned handlers, execution units, and span settings
|
|
47
|
+
* @returns A fully configured `ArvoEventHandler` instance for the given contract
|
|
59
48
|
*/
|
|
60
49
|
var createArvoEventHandler = function (param) { return new _1.default(param); };
|
|
61
50
|
exports.createArvoEventHandler = createArvoEventHandler;
|
|
@@ -4,76 +4,84 @@ import AbstractArvoEventHandler from '../AbstractArvoEventHandler';
|
|
|
4
4
|
import type { ArvoEventHandlerOpenTelemetryOptions } from '../types';
|
|
5
5
|
import type { ArvoEventHandlerFunction, IArvoEventHandler } from './types';
|
|
6
6
|
/**
|
|
7
|
-
* ArvoEventHandler is the
|
|
8
|
-
*
|
|
7
|
+
* `ArvoEventHandler` is the foundational component for building stateless,
|
|
8
|
+
* contract-bound services in the Arvo system.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* in its contract, or it will fail both at compile time and runtime.
|
|
10
|
+
* It enforces strict contract validation, version-aware handler resolution,
|
|
11
|
+
* and safe, observable event emission — all while maintaining type safety,
|
|
12
|
+
* traceability, and support for multi-domain workflows.
|
|
14
13
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* ## What It Does
|
|
15
|
+
* - Ensures incoming events match the contract's `type` and `dataschema`
|
|
16
|
+
* - Resolves the correct contract version using `dataschema`
|
|
17
|
+
* - Validates input and output data via Zod schemas
|
|
18
|
+
* - Executes the version-specific handler function
|
|
19
|
+
* - Emits one or more response events based on the handler result
|
|
20
|
+
* - Supports multi-domain broadcasting via `domain[]` on the emitted events
|
|
21
|
+
* - Automatically emits system error events (`sys.*.error`) on failure
|
|
22
|
+
* - Integrates deeply with OpenTelemetry for tracing and observability
|
|
19
23
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* ensures all conditions are met before and after processing.
|
|
24
|
+
* ## Error Boundaries
|
|
25
|
+
* ArvoEventHandler enforces a clear separation between:
|
|
23
26
|
*
|
|
24
|
-
*
|
|
27
|
+
* - **Violations** — structural, schema, or config errors that break the contract.
|
|
28
|
+
* These are thrown and must be handled explicitly by the caller.
|
|
25
29
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* 3. **Schema Validation**: Validates event data against the contract's accepts schema
|
|
29
|
-
* 4. **Handler Execution**: Invokes the version-specific handler implementation
|
|
30
|
-
* 5. **Response Processing**: Validates and structures handler output into events
|
|
31
|
-
* 6. **Domain Broadcasting**: Creates multiple events for multi-domain distribution if specified
|
|
32
|
-
* 7. **Routing Configuration**: Applies routing logic based on handler output and event context
|
|
33
|
-
* 8. **Telemetry Integration**: Records processing metrics and tracing information
|
|
30
|
+
* - **System Errors** — runtime exceptions during execution that are caught and
|
|
31
|
+
* emitted as standardized `sys.<contract>.error` events.
|
|
34
32
|
*
|
|
35
|
-
* ##
|
|
33
|
+
* ## Domain Broadcasting
|
|
34
|
+
* The handler supports multi-domain event distribution. When the handler
|
|
35
|
+
* returns an event with a `domain` array, it is broadcast to one or more
|
|
36
|
+
* routing contexts.
|
|
36
37
|
*
|
|
37
|
-
*
|
|
38
|
+
* ### System Error Domain Control
|
|
39
|
+
* By default, system error events are broadcast into the source event’s domain,
|
|
40
|
+
* the handler’s contract domain, and the `null` domain. This fallback ensures errors
|
|
41
|
+
* are visible across all relevant contexts. Developers can override this behavior
|
|
42
|
+
* using the optional `systemErrorDomain` field to specify an explicit set of
|
|
43
|
+
* domain values, including symbolic constants from {@link ArvoDomain}.
|
|
38
44
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
45
|
+
* ### Supported Domain Values:
|
|
46
|
+
* - A **concrete domain string** like `'audit.orders'` or `'human.review'`
|
|
47
|
+
* - `null` to emit with no domain (standard internal flow)
|
|
48
|
+
* - A **symbolic reference** from {@link ArvoDomain}
|
|
43
49
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* processing contexts (source event domain, handler contract domain, and null domain).
|
|
50
|
+
* ### Domain Resolution Rules:
|
|
51
|
+
* - Each item in the `domain` array is resolved via {@link resolveEventDomain}
|
|
52
|
+
* - Duplicate domains are deduplicated before emitting
|
|
53
|
+
* - If `domain` is omitted entirely, Arvo defaults to `[null]`
|
|
49
54
|
*
|
|
50
|
-
*
|
|
55
|
+
* ### Example:
|
|
56
|
+
* ```ts
|
|
57
|
+
* return {
|
|
58
|
+
* type: 'evt.user.registered',
|
|
59
|
+
* data: { ... },
|
|
60
|
+
* domain: ['analytics', ArvoDomain.FROM_TRIGGERING_EVENT, null]
|
|
61
|
+
* };
|
|
62
|
+
* ```
|
|
63
|
+
* This would emit at most 3 copies of the event, domained to:
|
|
64
|
+
* - `'analytics'`
|
|
65
|
+
* - the domain of the incoming event
|
|
66
|
+
* - no domain (default)
|
|
51
67
|
*
|
|
52
|
-
*
|
|
68
|
+
* ### Domain Usage Guidance
|
|
53
69
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* 2. **Undefined Resolution**: `undefined` elements resolve to: `event.domain ?? handler.contract.domain ?? null`
|
|
57
|
-
* 3. **Automatic Deduplication**: Duplicate domains are removed to prevent redundant events
|
|
58
|
-
* 4. **Default Behavior**: Omitted/undefined `domain` field defaults to `[null]` (single event, no domain)
|
|
70
|
+
* > **Avoid setting `contract.domain` unless fully intentional.**
|
|
71
|
+
* 99% emitted event should default to `null` (standard processing pipeline).
|
|
59
72
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* - `domain: ['analytics', undefined, null]` → Creates up to 3 events:
|
|
63
|
-
* - Event with `domain: 'analytics'`
|
|
64
|
-
* - Event with `domain: event.domain ?? handler.contract.domain ?? null`
|
|
65
|
-
* - Event with `domain: null`
|
|
66
|
-
* - `domain: [null]` → Single event with explicit no-domain routing
|
|
67
|
-
* - `domain: undefined` (or omitted) → Single event with `domain: null`
|
|
73
|
+
* Contract-level domains enforce implicit routing for every emitted event
|
|
74
|
+
* in that handler, making the behavior harder to override and debug.
|
|
68
75
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* -
|
|
72
|
-
* - Handler contract domain (`handler.contract.domain`)
|
|
73
|
-
* - No-domain context (`null`)
|
|
76
|
+
* Prefer:
|
|
77
|
+
* - Explicit per-event `domain` values in handler output
|
|
78
|
+
* - Using `null` or symbolic constants to control domain cleanly
|
|
74
79
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
80
|
+
* ## When to Use Domains
|
|
81
|
+
* Use domains when handling for specialized contexts:
|
|
82
|
+
* - `'human.review'` → for human-in-the-loop steps
|
|
83
|
+
* - `'analytics.workflow'` → to pipe events into observability systems
|
|
84
|
+
* - `'external.partner.sync'` → to route to external services
|
|
77
85
|
*/
|
|
78
86
|
export default class ArvoEventHandler<TContract extends ArvoContract> extends AbstractArvoEventHandler {
|
|
79
87
|
/** Contract instance that defines the event schema and validation rules */
|
|
@@ -86,6 +94,7 @@ export default class ArvoEventHandler<TContract extends ArvoContract> extends Ab
|
|
|
86
94
|
readonly handler: ArvoEventHandlerFunction<TContract>;
|
|
87
95
|
/** The source identifier for events produced by this handler */
|
|
88
96
|
get source(): TContract['type'];
|
|
97
|
+
readonly systemErrorDomain?: (string | null)[];
|
|
89
98
|
/**
|
|
90
99
|
* The contract-defined domain for this handler, used as the default domain for emitted events.
|
|
91
100
|
* Can be overridden by individual handler implementations for cross-domain workflows.
|