arvo-event-handler 3.0.3 → 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 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
  [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-white.svg)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
2
6
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
3
7
 
4
- # Arvo
8
+ ## Installation
5
9
 
6
- ## What is Arvo
10
+ Install the package along with its core dependency:
7
11
 
8
- Arvo is an opinionated approach to building event-driven systems. It's designed as a pattern and methodology rather than a rigid framework.
12
+ ```bash
13
+ npm install arvo-event-handler arvo-core
14
+ ```
9
15
 
10
- ## Principal
16
+ ```bash
17
+ yarn add arvo-event-handler arvo-core
18
+ ```
11
19
 
12
- The core principle of Arvo is to provide a solid foundation with enough flexibility for customization, allowing you to impose your own technical posture, including security measures, event brokerage, and telemetry. While Arvo offers a structured approach, it encourages developers to implement their own solutions if they believe they can improve upon or diverge from Arvo's principles.
20
+ ## The Event Handlers
13
21
 
14
- If you're looking to focus on results without getting bogged down in the nitty-gritty of event creation, handling, system state management, and telemetry, while also avoiding vendor lock-in, Arvo provides an excellent starting point. I believe, it strikes a balance between opinionated design and customization, making it an ideal choice for developers who want a head start in building event-driven systems without sacrificing flexibility.
22
+ The Arvo event handling architecture is based on three handler patterns.
15
23
 
16
- Key features of Arvo include:
24
+ ### 1. Simple Event Handler
17
25
 
18
- - Lightweight and unopinionated core
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
- Whether you're building a small microservice or a large-scale distributed system, my hope with Arvo is to offers you some of the tools and patterns to help you succeed in the world of event-driven architecture.
28
+ ### 2. State-machine based workflow orchestration
25
29
 
26
- ## Arvo suite
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
- Arvo is a collection of libraries which allows you to build the event driven system in the Arvo pattern. However, if you feel you don't have to use them or you can use them as you see fit.
32
+ ### 3. Dynamic stateful event handling and orchestration
29
33
 
30
- | Scope | NPM | Github | Documentation |
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
- # Arvo - Event Handler
36
+ ## Core Infrastructure Components
37
37
 
38
- This package contains the event handler primitive required to enable an Arvo Event Driven System. These are light weight classes and types which take care of event listening, contract bound type inference, distributed open telemetry and much more.
38
+ Beyond the three main handler patterns, the package includes essential infrastructure components that enable robust distributed system operation.
39
39
 
40
- ## Documentation & Resources
40
+ ### Memory - State Persistance
41
41
 
42
- | Source | Link |
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
- ## Installation
44
+ This package includes `SimpleMachineMemory` for development/ prototyping scenarios and provides example for implementing cloud-based production-ready distributed storage solutions.
49
45
 
50
- You can install the core package via `npm` or `yarn`
46
+ ### Error Handling
51
47
 
52
- ```bash
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
- ```bash
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
- ## Components
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
- There 2 main types of event handlers in Arvo event driven system
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
- - [ArvoEventHandler](src/ArvoEventHandler/README.md) is designed to facilitate the handling of events as per an `ArvoContract` (see [arvo-core](https://saadahmad123.github.io/arvo-core/documents/ArvoContract.html)). It provides a robust and flexible way to create, manage, and execute event handlers for Arvo-based event driven systems.
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
- ## Getting Started
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
- To start using Arvo Event Handlers in your project:
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
- - Install the package as shown in the Installation section.
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
- import {
84
- createArvoEventHandler,
85
- createMultiArvoEventHandler,
86
- createArvoEventRouter,
87
- } from 'arvo-event-handler';
88
- ```
65
+ ### Local Developement & Testing
89
66
 
90
- - Begin defining your events, contracts, and handlers using the provided classes.
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
- This package is available under the MIT License. For more details, refer to the [LICENSE.md](LICENSE.md) file in the project repository.
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
- ## Change Logs
78
+ ## Documentation and Resources
97
79
 
98
- For a detailed list of changes and updates, please refer to the [document](CHANGELOG.md) file.
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
  [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
107
108
  [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
108
109
  [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
109
- [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
110
110
  [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
111
111
  [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
112
112
  [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
113
- [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
113
+ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=vulnerabilities)](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
- * Create the instance of `ArvoEventHandler`
5
+ * Creates an instance of `ArvoEventHandler` for the specified versioned contract and handlers.
6
6
  *
7
- * > **Caution:** Don't use domained contracts unless it is fully intentional. Using domained
8
- * contracts causes implicit domain assignment which can be hard to track and confusing. For 99%
9
- * of the cases you dont need domained contracts
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
- * ```typescript
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
- * logToSpan({
27
- * level: 'WARN',
28
- * message: 'Domain mismatch detected'
29
- * }, span)
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, span }) => {
49
- * // Process event according to contract v2.0.0
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
- * Create the instance of `ArvoEventHandler`
9
+ * Creates an instance of `ArvoEventHandler` for the specified versioned contract and handlers.
10
10
  *
11
- * > **Caution:** Don't use domained contracts unless it is fully intentional. Using domained
12
- * contracts causes implicit domain assignment which can be hard to track and confusing. For 99%
13
- * of the cases you dont need domained contracts
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
- * ```typescript
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
- * logToSpan({
31
- * level: 'WARN',
32
- * message: 'Domain mismatch detected'
33
- * }, span)
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, span }) => {
53
- * // Process event according to contract v2.0.0
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 core component for processing events in the Arvo system. It enforces
8
- * contracts between services by ensuring that all events follow their specified formats and rules.
7
+ * `ArvoEventHandler` is the foundational component for building stateless,
8
+ * contract-bound services in the Arvo system.
9
9
  *
10
- * The handler is built on two fundamental patterns: Meyer's Design by Contract and Fowler's
11
- * Tolerant Reader. It binds to an ArvoContract that defines what events it can receive and send
12
- * across all versions. This versioning is strict - the handler must implement every version defined
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
- * Following the Tolerant Reader pattern, the handler accepts any incoming event but only processes
16
- * those that exactly match one of its contract versions. When an event matches, it's handled by
17
- * the specific implementation for that version. This approach maintains compatibility while
18
- * ensuring precise contract adherence.
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
- * The handler uses Zod for validation, automatically checking both incoming and outgoing events.
21
- * This means it not only verifies data formats but also applies default values where needed and
22
- * ensures all conditions are met before and after processing.
24
+ * ## Error Boundaries
25
+ * ArvoEventHandler enforces a clear separation between:
23
26
  *
24
- * ## Event Processing Lifecycle
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
- * 1. **Type Validation**: Ensures the incoming event type matches the handler's contract
27
- * 2. **Contract Resolution**: Extracts version from dataschema and resolves appropriate contract version
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
- * ## Error Handling Strategy
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
- * The handler divides issues into two distinct categories:
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
- * - **Violations** are serious contract breaches that indicate fundamental problems with how services
40
- * are communicating. These errors bubble up to the calling code, allowing developers to handle
41
- * these critical issues explicitly. Violations include contract mismatches, schema validation
42
- * failures, and configuration errors.
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
- * - **System Error Events** cover normal runtime errors that occur during event processing. These are
45
- * typically workflow-related issues that need to be reported back to the event's source but don't
46
- * indicate a broken contract. System errors are converted to structured error events and returned
47
- * in the response. **Multi-domain error broadcasting** ensures error events reach all relevant
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
- * ## Multi-Domain Event Broadcasting
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
- * The handler supports sophisticated multi-domain event distribution through array-based domain specification:
68
+ * ### Domain Usage Guidance
53
69
  *
54
- * ### Domain Assignment Rules:
55
- * 1. **Array Processing**: Each element in the `domain` array creates a separate ArvoEvent
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
- * ### Domain Patterns:
61
- * - `domain: ['domain1', 'domain2']` Creates 2 events: one for each domain
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
- * ### Error Broadcasting:
70
- * System errors are automatically broadcast to all relevant processing contexts:
71
- * - Source event domain (`event.domain`)
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
- * Duplicates are automatically removed, so if `event.domain === handler.contract.domain`,
76
- * only two error events are created instead of three.
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.