UNPKG

arvo-event-handler

Version:

Type-safe event handler system with versioning, telemetry, and contract validation for distributed Arvo event-driven architectures, featuring routing and multi-handler support.

203 lines (202 loc) 8.49 kB
import type { ArvoContract, ArvoEventData, ArvoOrchestratorEventTypeGen, ArvoSemanticVersion, CloudEventExtension, InferVersionedArvoContract, VersionedArvoContract } from 'arvo-core'; import type { Invert, IsNever, ParameterizedObject, UnknownActorLogic, Values } from 'xstate'; import type { z } from 'zod'; /** * Represents an extended context for Arvo XState machines, including additional properties * for volatile and internal data. * * @remarks * This type extends the base XState MachineContext with additional properties * to provide more flexibility and organization in storing machine-related data. * * The `$$` suffix in property names is used to indicate special storage objects within the context. * * @note * To avoid runtime errors, it is recommended not to use `arvo$$` object at all in the * machine context */ export type ArvoMachineContext = { arvo$$?: { volatile$$?: { [key: string]: any; eventQueue$$?: EnqueueArvoEventActionParam[]; }; }; }; /** * Represents the parameters for the emitArvoEvent action in ArvoXState. * This type defines a subset of properties from the CreateArvoEvent type, * specifically tailored for emitting an ArvoEvent within the state machine context. * * @remarks * The EmitArvoEventActionParam type is crucial for maintaining consistency and * type safety when emitting events in an ArvoXState machine. It ensures that * only relevant properties are included and properly typed. * ``` */ export type EnqueueArvoEventActionParam<TData extends ArvoEventData = ArvoEventData, TType extends string = string, TExtension extends CloudEventExtension = CloudEventExtension> = { /** * The domain configuration for multi-domain event broadcasting. * * When an event is emitted with a `domain` array, Arvo generates a separate ArvoEvent * for each resolved domain value. This enables parallel routing to multiple contexts * such as analytics, auditing, human-in-the-loop systems, or external integrations. * * **Accepted Values:** * - A concrete domain string (e.g. `'audit.orders'`) * - `null` for standard internal routing (no domain) * - A symbolic value from {@link ArvoDomain}. * * **Broadcasting Rules:** * - Each resolved domain in the array creates a separate ArvoEvent instance * - Duplicate resolved domains are automatically removed * - If the field is omitted, Arvo defaults to `[null]` * * **Examples:** * - `['analytics.orders', 'audit.orders']` → Creates two routed events * - `[ArvoDomain.FROM_TRIGGERING_EVENT, 'human.review', null]` → Mirrors source domain, routes to review, and standard consumer * - `[null]` → Emits a single event with no domain routing * - _Omitted_ → Same as `[null]` */ domain?: (string | null)[]; /** * Custom extensions for the CloudEvent. * Allows for additional metadata to be attached to the event. * * @remarks * Use this field to include any non-standard attributes that are not * covered by the core CloudEvent specification or Arvo extensions. */ __extensions?: TExtension; /** * Defines access controls for the event. * Can be a UserID, encrypted string, or key-value pairs. * * @remarks * This field is used to implement fine-grained access control on event * consumption. The exact format and interpretation may depend on your * system's access control mechanisms. */ accesscontrol?: string; /** * The event payload. This payload must be JSON serializable. * * @remarks * The data field contains the event-specific information. Ensure that * the structure of this data conforms to the schema specified in the * `dataschema` field, if provided. */ data: TData; /** * Identifies the schema that the `data` adheres to. * Must be a valid URI if present. * * @remarks * Use this field to provide a link to the schema definition for the * event data. This helps consumers understand and validate the event structure. */ dataschema?: string; /** * Indicates alternative recipients or destinations for events. * Must be a valid URI if present. * * @remarks * Use this field to implement event forwarding or to specify secondary * event consumers in addition to the primary one specified in the `to` field. */ redirectto?: string; /** * Defines the consumer machine of the event. Used for event routing. * Must be a valid URI if present. If not available, the `type` field * is used as a default. * * @remarks * This field is crucial for directing events to specific services or * components in your system. Ensure the URI is correctly formatted and * recognized by your event routing infrastructure. */ to?: string; /** * Describes the type of event. * Should be prefixed with a reverse-DNS name. * * @remarks * The event type is a key field for consumers to understand the nature * of the event without inspecting its data. Use a consistent naming convention * to enhance system-wide event comprehension. */ type: TType; /** * Represents the cost associated with generating the cloudevent. * * @remarks * By default, it uses the actor's executionunits. This field can be used for * resource accounting or billing purposes. Only override this if you have a specific * reason to assign a different cost to this particular event emission. */ executionunits?: number; }; /** * @remarks * This is an internal type. Copied as it is from the * xstate core [here](https://github.com/statelyai/xstate/blob/main/packages/core/src/setup.ts#L26) */ export type ToParameterizedObject<TParameterizedMap extends Record<string, ParameterizedObject['params'] | undefined>> = IsNever<TParameterizedMap> extends true ? never : Values<{ [K in keyof TParameterizedMap & string]: { type: K; params: TParameterizedMap[K]; }; }>; /** * @remarks * This is an internal type. Copied as it is from the * xstate core [here](https://github.com/statelyai/xstate/blob/main/packages/core/src/setup.ts#L43) */ export type ToProvidedActor<TChildrenMap extends Record<string, string>, TActors extends Record<string, UnknownActorLogic>> = IsNever<TActors> extends true ? never : Values<{ [K in keyof TActors & string]: { src: K; logic: TActors[K]; id: IsNever<TChildrenMap> extends true ? string | undefined : K extends keyof Invert<TChildrenMap> ? Invert<TChildrenMap>[K] & string : string | undefined; }; }>; /** * Infers emittable events from a versioned Arvo contract. * * @template T - Versioned Arvo contract type * * @remarks * Extracts all possible events that can be emitted by a contract, * including system error events. */ export type InferEmittableEventsFromVersionedArvoContract<T extends VersionedArvoContract<ArvoContract, ArvoSemanticVersion>> = { [K in keyof InferVersionedArvoContract<T>['emits']]: InferVersionedArvoContract<T>['emits'][K]; }[keyof InferVersionedArvoContract<T>['emits']] | InferVersionedArvoContract<T>['systemError']; /** * Extracts the orchestrator type from an event type string. * * @template T - Event type string * * @remarks * Parses the specific orchestrator type from a fully qualified event type string. */ export type ExtractOrchestratorType<T extends string> = T extends `${typeof ArvoOrchestratorEventTypeGen.prefix}.${infer Type}` ? Type : never; /** * Infers the complete service contract from a record of versioned Arvo contracts. * * @template T - Record of versioned Arvo contracts * * @remarks * Generates a comprehensive type definition including both emitted and received events * for all services in the contract. * * @property emitted - Events that can be emitted by the orchestrator * @property events - Events that can be received by the orchestrator */ export type InferServiceContract<T extends Record<string, VersionedArvoContract<ArvoContract, ArvoSemanticVersion>>> = { emitted: { [K in keyof T]: EnqueueArvoEventActionParam<z.input<T[K]['accepts']['schema']>, T[K]['accepts']['type']>; }[keyof T]; events: { [K in keyof T]: InferEmittableEventsFromVersionedArvoContract<T[K]>; }[keyof T]; };