UNPKG

arvo-event-handler

Version:

A complete set of orthogonal event handler and orchestration primitives for Arvo based applications, featuring declarative state machines (XState), imperative resumables for agentic workflows, contract-based routing, OpenTelemetry observability, and in-me

170 lines (169 loc) 9.05 kB
import { type ArvoOrchestratorEventTypeGen, type CreateArvoEvent, type InferVersionedArvoContract, type VersionedArvoContract } from 'arvo-core'; import { type ActionFunction, type MachineConfig, type MachineContext, type MetaObject, type ParameterizedObject, type SetupTypes } from 'xstate'; import type { z } from 'zod'; import ArvoMachine from '.'; import type { EnqueueArvoEventActionParam, ExtractOrchestratorType, InferServiceContract, ToParameterizedObject, ToProvidedActor } from './types'; import { NonEmptyArray } from '../types'; /** * Establishes the foundation for creating Arvo-compatible state machines. * * Designed for synchronous state machine orchestrations in Arvo's event-driven architecture. * Builds upon XState with Arvo-specific constraints to enforce predictable state transitions. * * @throws {ConfigViolation} When configuration violates Arvo constraints: * - Using `actors` or `delays` (async behavior not supported) * - Overriding reserved `enqueueArvoEvent` action name * - Machine version mismatch with contract version * - Using `invoke` or `after` in state configurations * - Service contracts with duplicate URIs (multiple versions of same contract) * - Circular dependency (self contract URI matches a service contract URI) */ export declare function setupArvoMachine<TContext extends MachineContext, TSelfContract extends VersionedArvoContract<any, any>, TServiceContracts extends Record<string, VersionedArvoContract<any, any>>, TActions extends Record<string, ParameterizedObject['params'] | undefined> = {}, TGuards extends Record<string, ParameterizedObject['params'] | undefined> = {}, TTag extends string = string, TMeta extends MetaObject = MetaObject>(param: { schemas?: unknown; /** * Contract definitions for the machine's event interface. * Defines what events the machine accepts, emits, and exchanges with services. */ contracts: { /** * Self contract defining the machine's initialization input structure * and the completion output structure when the machine finishes execution. */ self: TSelfContract; /** * Service contracts defining the event interfaces for external services. * Each service specifies the events it accepts and emits, enabling * type-safe communication between the machine and its dependencies. */ services: TServiceContracts; }; /** * Type definitions for the machine's internal structure. * Specifies the shape of context and other variables used throughout * the machine's lifecycle. These types enable full type inference and safety. */ types?: Omit<SetupTypes<TContext, InferServiceContract<TServiceContracts>['events'], {}, TTag, InferVersionedArvoContract<TSelfContract>['accepts']['data'], InferVersionedArvoContract<TSelfContract>['emits'][ReturnType<typeof ArvoOrchestratorEventTypeGen.complete<ExtractOrchestratorType<TSelfContract['accepts']['type']>>>]['data'], InferServiceContract<TServiceContracts>['emitted'], TMeta>, 'input' | 'output' | 'children' | 'emitted'>; /** * Named action implementations that can be referenced throughout the machine. * Actions perform side effects like data transformations, context updates, * and event emissions. Each action receives the current context and event, * along with any parameters defined in its type. * * For more information, see [xstate action docs](https://stately.ai/docs/actions) * * @example * ```typescript * actions: { * updateUser: ({ context, event }, params) => { * // Transform and update context * }, * logEvent: ({ event }) => { * // Log for debugging * } * } * ``` */ actions?: { [K in keyof TActions]: ActionFunction<TContext, InferServiceContract<TServiceContracts>['events'], InferServiceContract<TServiceContracts>['events'], TActions[K], never, ToParameterizedObject<TActions>, ToParameterizedObject<TGuards>, never, InferServiceContract<TServiceContracts>['emitted']>; }; /** * Named guard implementations that control conditional state transitions. * Guards are boolean functions that determine whether a transition should occur * based on the current context and event. They enable dynamic flow control * without side effects. * * For more information, see [xstate guard docs](https://stately.ai/docs/guards) * * @example * ```typescript * guards: { * isAuthorized: ({ context, event }, params) => { * return context.user.role === 'admin'; * }, * hasRequiredData: ({ context }) => { * return context.data !== null; * } * } * ``` */ guards?: { [K in keyof TGuards]: (args: { context: TContext; event: InferServiceContract<TServiceContracts>['events']; }, params: TGuards[K]) => boolean; }; }): { /** * Creates an Arvo-compatible state machine with the specified configuration. * * Constructs a fully-typed state machine that orchestrates event-driven workflows * using the contracts and types defined in setup. The machine enforces synchronous * execution and validates configuration against Arvo constraints. * * For more information, see [xstate state machine docs](https://stately.ai/docs/states) * @returns {ArvoMachine} A configured Arvo machine ready for execution * @throws {ConfigViolation} When configuration violates Arvo constraints (see {@link setupArvoMachine} docs) * * @example * ```typescript * const machine = setup.createMachine({ * id: 'machineV100', * initial: 'verifying', * context: ({ input }) => ({ * userId: input.data.userId, * verified: false * }), * states: { * verifying: { * on: { * 'com.user.verified': { * target: 'active', * actions: { type: 'updateUser' } * } * } * }, * active: { * type: 'final' * } * } * }); * ``` */ createMachine: <const TConfig extends MachineConfig<TContext, InferServiceContract<TServiceContracts>["events"], ToProvidedActor<{}, {}>, ToParameterizedObject<TActions & { enqueueArvoEvent: EnqueueArvoEventActionParam; }>, ToParameterizedObject<TGuards>, never, TTag, InferVersionedArvoContract<TSelfContract>["accepts"], z.input<TSelfContract["emits"][ReturnType<typeof ArvoOrchestratorEventTypeGen.complete<ExtractOrchestratorType<TSelfContract["accepts"]["type"]>>>]> & { __id?: CreateArvoEvent<Record<string, unknown>, string>["id"]; __executionunits?: CreateArvoEvent<Record<string, unknown>, string>["executionunits"]; __domain?: NonEmptyArray<string | null>; }, InferServiceContract<TServiceContracts>["emitted"], TMeta>>(config: TConfig & { id: string; version?: TSelfContract["version"]; }) => ArvoMachine<string, TSelfContract["version"], TSelfContract, TServiceContracts, import("xstate").StateMachine<TContext, { [K in keyof TServiceContracts]: import("./types").InferEmittableEventsFromVersionedArvoContract<TServiceContracts[K]>; }[keyof TServiceContracts], {}, never, (TActions & { enqueueArvoEvent: EnqueueArvoEventActionParam; } extends infer T extends Record<string, import("xstate").NonReducibleUnknown> ? { [K_1 in keyof T as K_1 & string]: { type: K_1 & string; params: T[K_1]; }; } : never)[keyof (TActions & { enqueueArvoEvent: EnqueueArvoEventActionParam; } extends infer T_1 extends Record<string, import("xstate").NonReducibleUnknown> ? { [K_1 in keyof T_1 as K_1 & string]: { type: K_1 & string; params: T_1[K_1]; }; } : never)], { [K_2 in keyof TGuards as K_2 & string]: { type: K_2 & string; params: TGuards[K_2]; }; }[keyof { [K_2 in keyof TGuards as K_2 & string]: { type: K_2 & string; params: TGuards[K_2]; }; }], never, {} | { [x: string]: {} | /*elided*/ any | { [x: string]: {} | /*elided*/ any | /*elided*/ any; }; } | { [x: string]: {} | { [x: string]: {} | /*elided*/ any | /*elided*/ any; } | /*elided*/ any; }, TTag, TSelfContract["accepts"]["schema"]["_output"], { [K_3 in string & keyof TSelfContract["emits"]]: import("arvo-core").InferArvoEvent<import("arvo-core").ArvoEvent<TSelfContract["emits"][K_3]["_output"], Record<string, any>, K_3>>; }[`arvo.orc.${ExtractOrchestratorType<TSelfContract["accepts"]["type"]>}.done`]["data"], { [K_4 in keyof TServiceContracts]: EnqueueArvoEventActionParam<z.input<TServiceContracts[K_4]["accepts"]["schema"]>, TServiceContracts[K_4]["accepts"]["type"], Record<string, string | number | boolean | null>>; }[keyof TServiceContracts], TMeta, { id: any; states: any; }>>; };