arvo-event-handler 2.3.3 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AbstractArvoEventHandler/index.d.ts +1 -1
- package/dist/ArvoEventHandler/helpers.d.ts +40 -6
- package/dist/ArvoEventHandler/helpers.js +40 -6
- package/dist/ArvoEventHandler/index.d.ts +78 -49
- package/dist/ArvoEventHandler/index.js +151 -82
- package/dist/ArvoEventHandler/types.d.ts +25 -2
- package/dist/ArvoMachine/createMachine.d.ts +208 -0
- package/dist/ArvoMachine/createMachine.js +283 -0
- package/dist/ArvoMachine/index.d.ts +93 -0
- package/dist/ArvoMachine/index.js +160 -0
- package/dist/ArvoMachine/types.d.ts +194 -0
- package/dist/ArvoMachine/utils.d.ts +40 -0
- package/dist/ArvoMachine/utils.js +70 -0
- package/dist/ArvoOrchestrator/error.d.ts +16 -0
- package/dist/ArvoOrchestrator/error.js +43 -0
- package/dist/ArvoOrchestrator/factory.d.ts +28 -0
- package/dist/ArvoOrchestrator/factory.js +56 -0
- package/dist/ArvoOrchestrator/index.d.ts +69 -0
- package/dist/ArvoOrchestrator/index.js +597 -0
- package/dist/ArvoOrchestrator/types.d.ts +98 -0
- package/dist/ArvoResumable/factory.d.ts +50 -0
- package/dist/ArvoResumable/factory.js +70 -0
- package/dist/ArvoResumable/index.d.ts +141 -0
- package/dist/ArvoResumable/index.js +694 -0
- package/dist/ArvoResumable/types.d.ts +147 -0
- package/dist/ArvoResumable/types.js +2 -0
- package/dist/MachineExecutionEngine/index.d.ts +29 -0
- package/dist/MachineExecutionEngine/index.js +132 -0
- package/dist/MachineExecutionEngine/interface.d.ts +14 -0
- package/dist/MachineExecutionEngine/interface.js +2 -0
- package/dist/MachineExecutionEngine/types.d.ts +14 -0
- package/dist/MachineExecutionEngine/types.js +2 -0
- package/dist/MachineMemory/Simple.d.ts +51 -0
- package/dist/MachineMemory/Simple.js +158 -0
- package/dist/MachineMemory/TelemetredSimple.d.ts +51 -0
- package/dist/MachineMemory/TelemetredSimple.js +230 -0
- package/dist/MachineMemory/interface.d.ts +57 -0
- package/dist/MachineMemory/interface.js +2 -0
- package/dist/MachineMemory/utils.d.ts +1 -0
- package/dist/MachineMemory/utils.js +18 -0
- package/dist/MachineRegistry/index.d.ts +37 -0
- package/dist/MachineRegistry/index.js +87 -0
- package/dist/MachineRegistry/interface.d.ts +21 -0
- package/dist/MachineRegistry/interface.js +2 -0
- package/dist/SyncEventResource/index.d.ts +110 -0
- package/dist/SyncEventResource/index.js +280 -0
- package/dist/SyncEventResource/types.d.ts +2 -0
- package/dist/SyncEventResource/types.js +2 -0
- package/dist/index.d.ts +26 -8
- package/dist/index.js +39 -16
- package/dist/utils/SimpleEventBroker/helper.d.ts +166 -0
- package/dist/utils/SimpleEventBroker/helper.js +276 -0
- package/dist/utils/SimpleEventBroker/index.d.ts +96 -0
- package/dist/utils/SimpleEventBroker/index.js +259 -0
- package/dist/utils/SimpleEventBroker/types.d.ts +6 -0
- package/dist/utils/SimpleEventBroker/types.js +2 -0
- package/dist/utils/SimpleEventBroker/utils.d.ts +1 -0
- package/dist/utils/SimpleEventBroker/utils.js +10 -0
- package/dist/{utils.d.ts → utils/index.d.ts} +3 -36
- package/dist/utils/index.js +91 -0
- package/dist/utils/object/index.d.ts +37 -0
- package/dist/utils/object/index.js +63 -0
- package/package.json +5 -3
- package/dist/ArvoEventRouter/helpers.d.ts +0 -19
- package/dist/ArvoEventRouter/helpers.js +0 -22
- package/dist/ArvoEventRouter/index.d.ts +0 -89
- package/dist/ArvoEventRouter/index.js +0 -268
- package/dist/ArvoEventRouter/types.d.ts +0 -36
- package/dist/ArvoEventRouter/utils.d.ts +0 -30
- package/dist/ArvoEventRouter/utils.js +0 -43
- package/dist/MultiArvoEventHandler/helpers.d.ts +0 -48
- package/dist/MultiArvoEventHandler/helpers.js +0 -56
- package/dist/MultiArvoEventHandler/index.d.ts +0 -68
- package/dist/MultiArvoEventHandler/index.js +0 -205
- package/dist/MultiArvoEventHandler/types.d.ts +0 -64
- package/dist/utils.js +0 -191
- /package/dist/{ArvoEventRouter → ArvoMachine}/types.js +0 -0
- /package/dist/{MultiArvoEventHandler → ArvoOrchestrator}/types.js +0 -0
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import type { Span, SpanOptions } from '@opentelemetry/api';
|
|
2
|
-
import type { ArvoContract, ArvoEvent, ArvoSemanticVersion, CreateArvoEvent, VersionedArvoContract } from 'arvo-core';
|
|
2
|
+
import type { ArvoContract, ArvoEvent, ArvoSemanticVersion, CreateArvoEvent, InferArvoEvent, VersionedArvoContract } from 'arvo-core';
|
|
3
3
|
import type { z } from 'zod';
|
|
4
4
|
/**
|
|
5
5
|
* Represents the input for an ArvoEvent handler function.
|
|
6
6
|
*/
|
|
7
7
|
export type ArvoEventHandlerFunctionInput<TContract extends VersionedArvoContract<any, any>> = {
|
|
8
8
|
/** The ArvoEvent object. */
|
|
9
|
-
event: ArvoEvent<z.infer<TContract['accepts']['schema']>, Record<string, any>, TContract['accepts']['type']
|
|
9
|
+
event: InferArvoEvent<ArvoEvent<z.infer<TContract['accepts']['schema']>, Record<string, any>, TContract['accepts']['type']>>;
|
|
10
10
|
/** The source field data of the handler */
|
|
11
11
|
source: string;
|
|
12
|
+
/** The domain information for handling the event */
|
|
13
|
+
domain: {
|
|
14
|
+
self: string | null;
|
|
15
|
+
event: string | null;
|
|
16
|
+
};
|
|
17
|
+
/** The contract used in the processing */
|
|
18
|
+
contract: TContract;
|
|
12
19
|
/** The OpenTelemetry span */
|
|
13
20
|
span: Span;
|
|
14
21
|
};
|
|
@@ -27,6 +34,22 @@ export type ArvoEventHandlerFunctionOutput<TContract extends VersionedArvoContra
|
|
|
27
34
|
executionunits?: number;
|
|
28
35
|
/** Optional extensions for the event. */
|
|
29
36
|
__extensions?: Record<string, string | number | boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* The event domain configuration for multi-domain broadcasting.
|
|
39
|
+
*
|
|
40
|
+
* **Domain Broadcasting Rules:**
|
|
41
|
+
* - Each element in the array creates a separate ArvoEvent instance
|
|
42
|
+
* - `undefined` elements resolve using inheritance: `event.domain ?? contract.domain ?? null`
|
|
43
|
+
* - Duplicate domains are automatically removed to prevent redundant events
|
|
44
|
+
* - Omitting this field (or setting to `undefined`) defaults to `[null]`
|
|
45
|
+
*
|
|
46
|
+
* **Domain Broadcasting Patterns:**
|
|
47
|
+
* - `['domain1', 'domain2']` → Creates 2 events for different processing contexts
|
|
48
|
+
* - `['analytics', undefined, 'audit']` → Creates events for analytics, inherited context, and audit
|
|
49
|
+
* - `[null]` → Creates single event with no domain routing (standard processing)
|
|
50
|
+
* - `undefined` (or omitted) → Creates single event with `domain: null`
|
|
51
|
+
*/
|
|
52
|
+
domain?: (string | undefined | null)[];
|
|
30
53
|
};
|
|
31
54
|
}[keyof TContract['emits']];
|
|
32
55
|
/**
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { type ArvoOrchestratorEventTypeGen, type InferVersionedArvoContract, type VersionedArvoContract } from 'arvo-core';
|
|
2
|
+
import { type ActionFunction, type MachineConfig, type MachineContext, type MetaObject, type ParameterizedObject, type SetupTypes } from 'xstate';
|
|
3
|
+
import type { z } from 'zod';
|
|
4
|
+
import ArvoMachine from '.';
|
|
5
|
+
import type { EnqueueArvoEventActionParam, ExtractOrchestratorType, InferServiceContract, ToParameterizedObject, ToProvidedActor } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Establishes the foundation for creating Arvo-compatible state machines.
|
|
8
|
+
*
|
|
9
|
+
* This function configures the core elements of an Arvo state machine, including
|
|
10
|
+
* built-in actions like `enqueueArvoEvent`, and enforces Arvo-specific constraints
|
|
11
|
+
* to ensure compatibility with the Arvo event-driven system.
|
|
12
|
+
*
|
|
13
|
+
* @param param - Configuration object for the machine setup
|
|
14
|
+
* @returns An object containing the `createMachine` function
|
|
15
|
+
* @throws {Error} If 'actors', 'delays', or reserved action names are used in the configuration
|
|
16
|
+
*
|
|
17
|
+
* @description
|
|
18
|
+
* `setupArvoMachine` is a crucial function in the Arvo ecosystem, designed to create
|
|
19
|
+
* synchronous state machine orchestrations for Arvo's event-driven architecture.
|
|
20
|
+
* It builds upon XState, providing a tailored implementation that:
|
|
21
|
+
*
|
|
22
|
+
* 1. Enforces synchronous behavior to maintain predictable state transitions
|
|
23
|
+
* 3. Implements Arvo-specific constraints and features
|
|
24
|
+
*
|
|
25
|
+
* Key features:
|
|
26
|
+
* - Synchronous execution: Ensures deterministic behavior in event-driven systems
|
|
27
|
+
* - Built-in actions: Includes `enqueueArvoEvent` for Arvo event handling
|
|
28
|
+
* - Constraint checking: Prevents usage of asynchronous features like 'actors' or 'delays'
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* While `setupArvoMachine` is based on XState's `setup` and `createMachine` functions,
|
|
32
|
+
* it includes Arvo-specific modifications and restrictions. For a deeper understanding
|
|
33
|
+
* of the underlying XState concepts, refer to the official XState documentation:
|
|
34
|
+
* - XState setup: https://stately.ai/docs/setup
|
|
35
|
+
* - XState createMachine: https://stately.ai/docs/machines
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* Here's a comprehensive example demonstrating how to use `setupArvoMachine`:
|
|
39
|
+
*
|
|
40
|
+
* ```typescript
|
|
41
|
+
* import { setupArvoMachine } from 'arvo-xstate'
|
|
42
|
+
* import { createArvoOrchestratorContract, ArvoErrorSchema, createArvoContract } from 'arvo-core'
|
|
43
|
+
* import { z } from 'zod'
|
|
44
|
+
*
|
|
45
|
+
* // Define the LLM orchestrator contract
|
|
46
|
+
* const llmContract = createArvoOrchestratorContract({
|
|
47
|
+
* uri: `#/orchestrators/llm/`,
|
|
48
|
+
* type: 'llm',
|
|
49
|
+
* versions: {
|
|
50
|
+
* '0.0.1': {
|
|
51
|
+
* init: z.object({
|
|
52
|
+
* request: z.string(),
|
|
53
|
+
* llm: z.enum(['gpt-4', 'gpt-4o']),
|
|
54
|
+
* }),
|
|
55
|
+
* complete: z.object({
|
|
56
|
+
* response: z.string(),
|
|
57
|
+
* })
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* })
|
|
61
|
+
*
|
|
62
|
+
* // Define the OpenAI service contract
|
|
63
|
+
* const openAiContract = createArvoContract({
|
|
64
|
+
* uri: `#/services/openai`,
|
|
65
|
+
* type: 'com.openai.completions',
|
|
66
|
+
* versions: {
|
|
67
|
+
* '0.0.1': {
|
|
68
|
+
* accepts: z.object({
|
|
69
|
+
* request: z.string()
|
|
70
|
+
* }),
|
|
71
|
+
* emits: {
|
|
72
|
+
* 'evt.openai.completions.success': z.object({
|
|
73
|
+
* response: z.string(),
|
|
74
|
+
* })
|
|
75
|
+
* }
|
|
76
|
+
* }
|
|
77
|
+
* }
|
|
78
|
+
* })
|
|
79
|
+
*
|
|
80
|
+
* const machineId = 'machineV100'
|
|
81
|
+
*
|
|
82
|
+
* // Set up the Arvo machine
|
|
83
|
+
* const llmMachine = setupArvoMachine({
|
|
84
|
+
* contracts: {
|
|
85
|
+
* self: llmContract.version('0.0.1'),
|
|
86
|
+
* services: {
|
|
87
|
+
* openAiContract.version('0.0.1'),
|
|
88
|
+
* }
|
|
89
|
+
* },
|
|
90
|
+
* types: {
|
|
91
|
+
* context: {} as {
|
|
92
|
+
* request: string,
|
|
93
|
+
* llm: string,
|
|
94
|
+
* response: string | null,
|
|
95
|
+
* errors: z.infer<typeof ArvoErrorSchema>[]
|
|
96
|
+
* },
|
|
97
|
+
* tags: {} as 'pending' | 'success' | 'error',
|
|
98
|
+
* },
|
|
99
|
+
* actions: {
|
|
100
|
+
* log: ({context, event}) => console.log({context, event})
|
|
101
|
+
* },
|
|
102
|
+
* guards: {
|
|
103
|
+
* isValid: ({context, event}) => Boolean(context.request)
|
|
104
|
+
* }
|
|
105
|
+
* }).createMachine({
|
|
106
|
+
* id: machineId,
|
|
107
|
+
* context: ({input}) => ({
|
|
108
|
+
* request: input.request,
|
|
109
|
+
* llm: input.llm,
|
|
110
|
+
* response: null,
|
|
111
|
+
* errors: [],
|
|
112
|
+
* }),
|
|
113
|
+
* initial: 'validate',
|
|
114
|
+
* states: {
|
|
115
|
+
* validate: {
|
|
116
|
+
* always: [
|
|
117
|
+
* {
|
|
118
|
+
* guard: 'isValid',
|
|
119
|
+
* target: 'llm',
|
|
120
|
+
* },
|
|
121
|
+
* {
|
|
122
|
+
* target: 'error',
|
|
123
|
+
* }
|
|
124
|
+
* ]
|
|
125
|
+
* },
|
|
126
|
+
* llm: {
|
|
127
|
+
* entry: [
|
|
128
|
+
* {
|
|
129
|
+
* type: 'log',
|
|
130
|
+
* },
|
|
131
|
+
* emit(({context}) => ({
|
|
132
|
+
* type: 'com.openai.completions',
|
|
133
|
+
* data: {
|
|
134
|
+
* request: context.request,
|
|
135
|
+
* },
|
|
136
|
+
* }))
|
|
137
|
+
* ],
|
|
138
|
+
* on: {
|
|
139
|
+
* 'evt.openai.completions.success': {
|
|
140
|
+
* actions: [
|
|
141
|
+
* assign({response: ({event}) => event.response})
|
|
142
|
+
* ],
|
|
143
|
+
* target: 'done'
|
|
144
|
+
* },
|
|
145
|
+
* 'sys.com.openai.completions.error': {
|
|
146
|
+
* actions: [
|
|
147
|
+
* assign({errors: ({context, event}) => [...context.errors, event.body]})
|
|
148
|
+
* ],
|
|
149
|
+
* target: 'error'
|
|
150
|
+
* }
|
|
151
|
+
* }
|
|
152
|
+
* },
|
|
153
|
+
* done: {
|
|
154
|
+
* type: 'final'
|
|
155
|
+
* },
|
|
156
|
+
* error: {
|
|
157
|
+
* type: 'final'
|
|
158
|
+
* },
|
|
159
|
+
* }
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* This example demonstrates:
|
|
164
|
+
* 1. Defining Arvo contracts for the orchestrator and a service
|
|
165
|
+
* 2. Setting up an Arvo machine with contracts, types, actions, and guards
|
|
166
|
+
* 3. Creating a machine with states for validation, LLM interaction, and error handling
|
|
167
|
+
* 4. Using XState features like `emit` bound with Arvo contracts for event emitting and event handling via transitions
|
|
168
|
+
*/
|
|
169
|
+
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: {
|
|
170
|
+
schemas?: unknown;
|
|
171
|
+
contracts: {
|
|
172
|
+
self: TSelfContract;
|
|
173
|
+
services: TServiceContracts;
|
|
174
|
+
};
|
|
175
|
+
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'>;
|
|
176
|
+
actions?: {
|
|
177
|
+
[K in keyof TActions]: ActionFunction<TContext, InferServiceContract<TServiceContracts>['events'], InferServiceContract<TServiceContracts>['events'], TActions[K], never, ToParameterizedObject<TActions>, ToParameterizedObject<TGuards>, never, InferServiceContract<TServiceContracts>['emitted']>;
|
|
178
|
+
};
|
|
179
|
+
guards?: {
|
|
180
|
+
[K in keyof TGuards]: (args: {
|
|
181
|
+
context: TContext;
|
|
182
|
+
event: InferServiceContract<TServiceContracts>['events'];
|
|
183
|
+
}, params: TGuards[K]) => boolean;
|
|
184
|
+
};
|
|
185
|
+
}): {
|
|
186
|
+
createMachine: <const TConfig extends MachineConfig<TContext, InferServiceContract<TServiceContracts>["events"], ToProvidedActor<{}, {}>, ToParameterizedObject<TActions & {
|
|
187
|
+
enqueueArvoEvent: EnqueueArvoEventActionParam;
|
|
188
|
+
}>, ToParameterizedObject<TGuards>, never, TTag, InferVersionedArvoContract<TSelfContract>["accepts"], z.input<TSelfContract["emits"][ReturnType<typeof ArvoOrchestratorEventTypeGen.complete<ExtractOrchestratorType<TSelfContract["accepts"]["type"]>>>]>, InferServiceContract<TServiceContracts>["emitted"], TMeta>>(config: TConfig & {
|
|
189
|
+
id: string;
|
|
190
|
+
version?: TSelfContract["version"];
|
|
191
|
+
}) => ArvoMachine<string, TSelfContract["version"], TSelfContract, TServiceContracts, import("xstate").StateMachine<TContext, { [K in keyof TServiceContracts]: import("./types").InferEmittableEventsFromVersionedArvoContract<TServiceContracts[K]>; }[keyof TServiceContracts], {}, never, import("xstate").IsNever<TActions & {
|
|
192
|
+
enqueueArvoEvent: EnqueueArvoEventActionParam;
|
|
193
|
+
}> extends true ? never : import("xstate").Values<{ [K_1 in (keyof TActions & string) | "enqueueArvoEvent"]: {
|
|
194
|
+
type: K_1;
|
|
195
|
+
params: (TActions & {
|
|
196
|
+
enqueueArvoEvent: EnqueueArvoEventActionParam;
|
|
197
|
+
})[K_1];
|
|
198
|
+
}; }>, import("xstate").IsNever<TGuards> extends true ? never : import("xstate").Values<{ [K_2 in keyof TGuards & string]: {
|
|
199
|
+
type: K_2;
|
|
200
|
+
params: TGuards[K_2];
|
|
201
|
+
}; }>, never, {} | {
|
|
202
|
+
[x: string]: {} | any | {
|
|
203
|
+
[x: string]: {} | any | any;
|
|
204
|
+
};
|
|
205
|
+
} | {
|
|
206
|
+
[x: string]: {} | any | any;
|
|
207
|
+
}, 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, any>>;
|
|
208
|
+
};
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
14
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
15
|
+
if (ar || !(i in from)) {
|
|
16
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
17
|
+
ar[i] = from[i];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
21
|
+
};
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.setupArvoMachine = setupArvoMachine;
|
|
27
|
+
var arvo_core_1 = require("arvo-core");
|
|
28
|
+
var xstate_1 = require("xstate");
|
|
29
|
+
var _1 = __importDefault(require("."));
|
|
30
|
+
var object_1 = require("../utils/object");
|
|
31
|
+
var utils_1 = require("./utils");
|
|
32
|
+
var uuid_1 = require("uuid");
|
|
33
|
+
/**
|
|
34
|
+
* Establishes the foundation for creating Arvo-compatible state machines.
|
|
35
|
+
*
|
|
36
|
+
* This function configures the core elements of an Arvo state machine, including
|
|
37
|
+
* built-in actions like `enqueueArvoEvent`, and enforces Arvo-specific constraints
|
|
38
|
+
* to ensure compatibility with the Arvo event-driven system.
|
|
39
|
+
*
|
|
40
|
+
* @param param - Configuration object for the machine setup
|
|
41
|
+
* @returns An object containing the `createMachine` function
|
|
42
|
+
* @throws {Error} If 'actors', 'delays', or reserved action names are used in the configuration
|
|
43
|
+
*
|
|
44
|
+
* @description
|
|
45
|
+
* `setupArvoMachine` is a crucial function in the Arvo ecosystem, designed to create
|
|
46
|
+
* synchronous state machine orchestrations for Arvo's event-driven architecture.
|
|
47
|
+
* It builds upon XState, providing a tailored implementation that:
|
|
48
|
+
*
|
|
49
|
+
* 1. Enforces synchronous behavior to maintain predictable state transitions
|
|
50
|
+
* 3. Implements Arvo-specific constraints and features
|
|
51
|
+
*
|
|
52
|
+
* Key features:
|
|
53
|
+
* - Synchronous execution: Ensures deterministic behavior in event-driven systems
|
|
54
|
+
* - Built-in actions: Includes `enqueueArvoEvent` for Arvo event handling
|
|
55
|
+
* - Constraint checking: Prevents usage of asynchronous features like 'actors' or 'delays'
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* While `setupArvoMachine` is based on XState's `setup` and `createMachine` functions,
|
|
59
|
+
* it includes Arvo-specific modifications and restrictions. For a deeper understanding
|
|
60
|
+
* of the underlying XState concepts, refer to the official XState documentation:
|
|
61
|
+
* - XState setup: https://stately.ai/docs/setup
|
|
62
|
+
* - XState createMachine: https://stately.ai/docs/machines
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* Here's a comprehensive example demonstrating how to use `setupArvoMachine`:
|
|
66
|
+
*
|
|
67
|
+
* ```typescript
|
|
68
|
+
* import { setupArvoMachine } from 'arvo-xstate'
|
|
69
|
+
* import { createArvoOrchestratorContract, ArvoErrorSchema, createArvoContract } from 'arvo-core'
|
|
70
|
+
* import { z } from 'zod'
|
|
71
|
+
*
|
|
72
|
+
* // Define the LLM orchestrator contract
|
|
73
|
+
* const llmContract = createArvoOrchestratorContract({
|
|
74
|
+
* uri: `#/orchestrators/llm/`,
|
|
75
|
+
* type: 'llm',
|
|
76
|
+
* versions: {
|
|
77
|
+
* '0.0.1': {
|
|
78
|
+
* init: z.object({
|
|
79
|
+
* request: z.string(),
|
|
80
|
+
* llm: z.enum(['gpt-4', 'gpt-4o']),
|
|
81
|
+
* }),
|
|
82
|
+
* complete: z.object({
|
|
83
|
+
* response: z.string(),
|
|
84
|
+
* })
|
|
85
|
+
* }
|
|
86
|
+
* }
|
|
87
|
+
* })
|
|
88
|
+
*
|
|
89
|
+
* // Define the OpenAI service contract
|
|
90
|
+
* const openAiContract = createArvoContract({
|
|
91
|
+
* uri: `#/services/openai`,
|
|
92
|
+
* type: 'com.openai.completions',
|
|
93
|
+
* versions: {
|
|
94
|
+
* '0.0.1': {
|
|
95
|
+
* accepts: z.object({
|
|
96
|
+
* request: z.string()
|
|
97
|
+
* }),
|
|
98
|
+
* emits: {
|
|
99
|
+
* 'evt.openai.completions.success': z.object({
|
|
100
|
+
* response: z.string(),
|
|
101
|
+
* })
|
|
102
|
+
* }
|
|
103
|
+
* }
|
|
104
|
+
* }
|
|
105
|
+
* })
|
|
106
|
+
*
|
|
107
|
+
* const machineId = 'machineV100'
|
|
108
|
+
*
|
|
109
|
+
* // Set up the Arvo machine
|
|
110
|
+
* const llmMachine = setupArvoMachine({
|
|
111
|
+
* contracts: {
|
|
112
|
+
* self: llmContract.version('0.0.1'),
|
|
113
|
+
* services: {
|
|
114
|
+
* openAiContract.version('0.0.1'),
|
|
115
|
+
* }
|
|
116
|
+
* },
|
|
117
|
+
* types: {
|
|
118
|
+
* context: {} as {
|
|
119
|
+
* request: string,
|
|
120
|
+
* llm: string,
|
|
121
|
+
* response: string | null,
|
|
122
|
+
* errors: z.infer<typeof ArvoErrorSchema>[]
|
|
123
|
+
* },
|
|
124
|
+
* tags: {} as 'pending' | 'success' | 'error',
|
|
125
|
+
* },
|
|
126
|
+
* actions: {
|
|
127
|
+
* log: ({context, event}) => console.log({context, event})
|
|
128
|
+
* },
|
|
129
|
+
* guards: {
|
|
130
|
+
* isValid: ({context, event}) => Boolean(context.request)
|
|
131
|
+
* }
|
|
132
|
+
* }).createMachine({
|
|
133
|
+
* id: machineId,
|
|
134
|
+
* context: ({input}) => ({
|
|
135
|
+
* request: input.request,
|
|
136
|
+
* llm: input.llm,
|
|
137
|
+
* response: null,
|
|
138
|
+
* errors: [],
|
|
139
|
+
* }),
|
|
140
|
+
* initial: 'validate',
|
|
141
|
+
* states: {
|
|
142
|
+
* validate: {
|
|
143
|
+
* always: [
|
|
144
|
+
* {
|
|
145
|
+
* guard: 'isValid',
|
|
146
|
+
* target: 'llm',
|
|
147
|
+
* },
|
|
148
|
+
* {
|
|
149
|
+
* target: 'error',
|
|
150
|
+
* }
|
|
151
|
+
* ]
|
|
152
|
+
* },
|
|
153
|
+
* llm: {
|
|
154
|
+
* entry: [
|
|
155
|
+
* {
|
|
156
|
+
* type: 'log',
|
|
157
|
+
* },
|
|
158
|
+
* emit(({context}) => ({
|
|
159
|
+
* type: 'com.openai.completions',
|
|
160
|
+
* data: {
|
|
161
|
+
* request: context.request,
|
|
162
|
+
* },
|
|
163
|
+
* }))
|
|
164
|
+
* ],
|
|
165
|
+
* on: {
|
|
166
|
+
* 'evt.openai.completions.success': {
|
|
167
|
+
* actions: [
|
|
168
|
+
* assign({response: ({event}) => event.response})
|
|
169
|
+
* ],
|
|
170
|
+
* target: 'done'
|
|
171
|
+
* },
|
|
172
|
+
* 'sys.com.openai.completions.error': {
|
|
173
|
+
* actions: [
|
|
174
|
+
* assign({errors: ({context, event}) => [...context.errors, event.body]})
|
|
175
|
+
* ],
|
|
176
|
+
* target: 'error'
|
|
177
|
+
* }
|
|
178
|
+
* }
|
|
179
|
+
* },
|
|
180
|
+
* done: {
|
|
181
|
+
* type: 'final'
|
|
182
|
+
* },
|
|
183
|
+
* error: {
|
|
184
|
+
* type: 'final'
|
|
185
|
+
* },
|
|
186
|
+
* }
|
|
187
|
+
* });
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* This example demonstrates:
|
|
191
|
+
* 1. Defining Arvo contracts for the orchestrator and a service
|
|
192
|
+
* 2. Setting up an Arvo machine with contracts, types, actions, and guards
|
|
193
|
+
* 3. Creating a machine with states for validation, LLM interaction, and error handling
|
|
194
|
+
* 4. Using XState features like `emit` bound with Arvo contracts for event emitting and event handling via transitions
|
|
195
|
+
*/
|
|
196
|
+
function setupArvoMachine(param) {
|
|
197
|
+
var _a;
|
|
198
|
+
var _b, _c;
|
|
199
|
+
var createConfigErrorMessage = function (type) {
|
|
200
|
+
return (0, arvo_core_1.cleanString)("\n Configuration Error: '".concat(type, "' not supported in Arvo machines\n \n Arvo machines do not support XState ").concat(type === 'actor' ? 'actors' : 'delay transitions', " as they introduce asynchronous behavior.\n \n To fix:\n 1. Remove the '").concat(type, "' configuration\n 2. Use Arvo's event-driven patterns instead for asynchronous operations\n "));
|
|
201
|
+
};
|
|
202
|
+
if (param.actors) {
|
|
203
|
+
throw new Error(createConfigErrorMessage('actor'));
|
|
204
|
+
}
|
|
205
|
+
if (param.delays) {
|
|
206
|
+
throw new Error(createConfigErrorMessage('delays'));
|
|
207
|
+
}
|
|
208
|
+
if ((_b = param.actions) === null || _b === void 0 ? void 0 : _b.enqueueArvoEvent) {
|
|
209
|
+
throw new Error((0, arvo_core_1.cleanString)("\n Configuration Error: Reserved action name 'enqueueArvoEvent'\n \n 'enqueueArvoEvent' is an internal Arvo system action and cannot be overridden.\n \n To fix: Use a different name for your action, such as:\n - 'queueCustomEvent'\n - 'scheduleEvent'\n - 'dispatchEvent'\n "));
|
|
210
|
+
}
|
|
211
|
+
var __areServiceContractsUnique = (0, utils_1.areServiceContractsUnique)(param.contracts.services);
|
|
212
|
+
if (!__areServiceContractsUnique.result) {
|
|
213
|
+
throw new Error("The service contracts must have unique URIs. Multiple versions of the same contract are not allow. The contracts '".concat(__areServiceContractsUnique.keys[0], "' and '").concat(__areServiceContractsUnique.keys[1], "' have the same URI '").concat(__areServiceContractsUnique.contractUri, "'"));
|
|
214
|
+
}
|
|
215
|
+
var __checkIfSelfIsAService = (0, utils_1.areServiceContractsUnique)(__assign(__assign({}, param.contracts.services), (_a = {}, _a[(0, uuid_1.v4)()] = param.contracts.self, _a)));
|
|
216
|
+
if (!__checkIfSelfIsAService.result) {
|
|
217
|
+
throw new Error("Circular dependency detected: Machine with URI '".concat(param.contracts.self.uri, "' is registered as service '").concat(__checkIfSelfIsAService.keys[1], "'. Self-referential services create execution loops and are prohibited."));
|
|
218
|
+
}
|
|
219
|
+
var combinedActions = __assign(__assign({}, ((_c = param.actions) !== null && _c !== void 0 ? _c : {})), { enqueueArvoEvent: (0, xstate_1.assign)(function (_a, param) {
|
|
220
|
+
var _b, _c, _d, _e, _f;
|
|
221
|
+
var context = _a.context;
|
|
222
|
+
return (__assign(__assign({}, (context !== null && context !== void 0 ? context : {})), { arvo$$: __assign(__assign({}, ((_b = context === null || context === void 0 ? void 0 : context.arvo$$) !== null && _b !== void 0 ? _b : {})), { volatile$$: __assign(__assign({}, ((_d = (_c = context === null || context === void 0 ? void 0 : context.arvo$$) === null || _c === void 0 ? void 0 : _c.volatile$$) !== null && _d !== void 0 ? _d : {})), { eventQueue$$: __spreadArray(__spreadArray([], (((_f = (_e = context === null || context === void 0 ? void 0 : context.arvo$$) === null || _e === void 0 ? void 0 : _e.volatile$$) === null || _f === void 0 ? void 0 : _f.eventQueue$$) || []), true), [param], false) }) }) }));
|
|
223
|
+
}) });
|
|
224
|
+
// Call the original setup function with modified parameters
|
|
225
|
+
var systemSetup = (0, xstate_1.setup)({
|
|
226
|
+
schemas: param.schemas,
|
|
227
|
+
types: param.types,
|
|
228
|
+
guards: param.guards,
|
|
229
|
+
actions: combinedActions,
|
|
230
|
+
});
|
|
231
|
+
/**
|
|
232
|
+
* Creates an Arvo-compatible XState machine.
|
|
233
|
+
*
|
|
234
|
+
* @param config - The configuration object for the machine
|
|
235
|
+
* @returns An ArvoMachine instance
|
|
236
|
+
*
|
|
237
|
+
* @throws Error if 'invoke' or 'after' configurations are used
|
|
238
|
+
*
|
|
239
|
+
* @remarks
|
|
240
|
+
* This function creates a state machine based on the provided configuration.
|
|
241
|
+
* It performs additional checks to ensure the machine adheres to Arvo's constraints,
|
|
242
|
+
* such as disallowing 'invoke' and 'after' configurations which could introduce
|
|
243
|
+
* asynchronous behavior.
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
var createMachine = function (config) {
|
|
247
|
+
var _a, _b;
|
|
248
|
+
var machineVersion = (_a = config.version) !== null && _a !== void 0 ? _a : param.contracts.self.version;
|
|
249
|
+
if (machineVersion !== param.contracts.self.version) {
|
|
250
|
+
throw new Error("Version mismatch: Machine version must be '".concat(param.contracts.self.version, "' or undefined, received '").concat(config.version, "'"));
|
|
251
|
+
}
|
|
252
|
+
var createConfigErrorMessage = function (type, path) {
|
|
253
|
+
var location = path.join(' > ');
|
|
254
|
+
if (type === 'invoke') {
|
|
255
|
+
return (0, arvo_core_1.cleanString)("\n Configuration Error: 'invoke' not supported\n \n Location: ".concat(location, "\n \n Arvo machines do not support XState invocations as they introduce asynchronous behavior.\n \n To fix: Replace 'invoke' with Arvo event-driven patterns for asynchronous operations\n "));
|
|
256
|
+
}
|
|
257
|
+
if (type === 'after') {
|
|
258
|
+
return (0, arvo_core_1.cleanString)("\n Configuration Error: 'after' not supported\n \n Location: ".concat(location, "\n \n Arvo machines do not support delayed transitions as they introduce asynchronous behavior.\n \n To fix: Replace 'after' with Arvo event-driven patterns for time-based operations\n "));
|
|
259
|
+
}
|
|
260
|
+
if (type === 'enqueueArvoEvent') {
|
|
261
|
+
return (0, arvo_core_1.cleanString)("\n Configuration Error: Reserved action name 'enqueueArvoEvent'\n \n Location: ".concat(location, "\n \n 'enqueueArvoEvent' is an internal Arvo system action and cannot be used in machine configurations.\n \n To fix: Use a different name for your action\n "));
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
for (var _i = 0, _c = (0, object_1.getAllPaths)((_b = config.states) !== null && _b !== void 0 ? _b : {}); _i < _c.length; _i++) {
|
|
265
|
+
var item = _c[_i];
|
|
266
|
+
if (item.path.includes('invoke')) {
|
|
267
|
+
throw new Error(createConfigErrorMessage('invoke', item.path));
|
|
268
|
+
}
|
|
269
|
+
if (item.path.includes('after')) {
|
|
270
|
+
throw new Error(createConfigErrorMessage('after', item.path));
|
|
271
|
+
}
|
|
272
|
+
if (item.path.includes('enqueueArvoEvent')) {
|
|
273
|
+
throw new Error(createConfigErrorMessage('enqueueArvoEvent', item.path));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
var machine = systemSetup.createMachine(__assign({}, config));
|
|
277
|
+
var hasParallelStates = (0, utils_1.detectParallelStates)(machine.config);
|
|
278
|
+
var hasMultipleNonSystemErrorEvents = Object.values(param.contracts.services).some(function (item) { return Object.keys(item.emits).length > 1; });
|
|
279
|
+
var requiresLocking = hasParallelStates || hasMultipleNonSystemErrorEvents;
|
|
280
|
+
return new _1.default(config.id, machineVersion, param.contracts, machine, requiresLocking);
|
|
281
|
+
};
|
|
282
|
+
return { createMachine: createMachine };
|
|
283
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type ArvoContract, type ArvoEvent, type ArvoOrchestratorContract, type ArvoSemanticVersion, type VersionedArvoContract } from 'arvo-core';
|
|
2
|
+
import type { AnyActorLogic } from 'xstate';
|
|
3
|
+
import type { z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Represents an ArvoMachine object that can be consumed by an Arvo orchestrator.
|
|
6
|
+
* ArvoMachine encapsulates the logic and metadata required for an Arvo-compatible
|
|
7
|
+
* state machine. It combines XState's actor logic with Arvo-specific contracts
|
|
8
|
+
* and versioning information.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* It is strongly recommended to use `setupArvoMachine(...).createMachine(...)`
|
|
12
|
+
* instead of creating this object directly. The setup function provides additional
|
|
13
|
+
* type safety and validation that helps prevent runtime errors.
|
|
14
|
+
*/
|
|
15
|
+
export default class ArvoMachine<TId extends string, TVersion extends ArvoSemanticVersion, TSelfContract extends VersionedArvoContract<ArvoOrchestratorContract, TVersion>, TServiceContract extends Record<string, VersionedArvoContract<ArvoContract, ArvoSemanticVersion>>, TLogic extends AnyActorLogic> {
|
|
16
|
+
readonly id: TId;
|
|
17
|
+
readonly version: TVersion;
|
|
18
|
+
readonly contracts: {
|
|
19
|
+
self: TSelfContract;
|
|
20
|
+
services: TServiceContract;
|
|
21
|
+
};
|
|
22
|
+
readonly logic: TLogic;
|
|
23
|
+
readonly requiresResourceLocking: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new ArvoMachine instance.
|
|
26
|
+
*
|
|
27
|
+
* @param id - A unique identifier for the machine. This ID must be unique within
|
|
28
|
+
* the scope of an orchestrator and is used for routing and logging.
|
|
29
|
+
*
|
|
30
|
+
* @param version - The semantic version of the machine. Must follow semver format
|
|
31
|
+
* and match the version specified in the contract.
|
|
32
|
+
*
|
|
33
|
+
* @param contracts - Configuration object containing contract definitions
|
|
34
|
+
* @param contracts.self - The contract defining this machine's interface and capabilities
|
|
35
|
+
* @param contracts.services - Record of contracts for services this machine can interact with
|
|
36
|
+
*
|
|
37
|
+
* @param logic - The XState actor logic that defines the machine's behavior,
|
|
38
|
+
* including states, transitions, and actions.
|
|
39
|
+
* @param [requiresResourceLocking] - Optional flag indicating if the machine needs distributed locks.
|
|
40
|
+
* False when machine has no parallel states and executes sequentially.
|
|
41
|
+
* Defaults to true.
|
|
42
|
+
*
|
|
43
|
+
* @throws {Error} When contracts are invalid or incompatible with the specified version
|
|
44
|
+
*/
|
|
45
|
+
constructor(id: TId, version: TVersion, contracts: {
|
|
46
|
+
self: TSelfContract;
|
|
47
|
+
services: TServiceContract;
|
|
48
|
+
}, logic: TLogic, requiresResourceLocking?: boolean);
|
|
49
|
+
/**
|
|
50
|
+
* Gets the event type that this machine accepts, as defined in its contract.
|
|
51
|
+
*/
|
|
52
|
+
get source(): TSelfContract['accepts']['type'];
|
|
53
|
+
/**
|
|
54
|
+
* Validates an event against the machine's contracts and data schemas.
|
|
55
|
+
* Performs validation for both self-contract events and service contract events.
|
|
56
|
+
*
|
|
57
|
+
* @param event - The event to validate
|
|
58
|
+
* @returns A validation result object:
|
|
59
|
+
* - "VALID" - Event is valid and can be processed
|
|
60
|
+
* - "CONTRACT_UNRESOLVED" - No matching contract found for the event
|
|
61
|
+
* - "INVALID" - Event dataschema conflict with contract
|
|
62
|
+
* - "INVALID_DATA" - Event data conflicts with contract
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const result = machine.validateInput(event);
|
|
67
|
+
* if (result.type === "VALID") {
|
|
68
|
+
* // Process the event
|
|
69
|
+
* } else if (result.type === "INVALID") {
|
|
70
|
+
* console.error(result.error);
|
|
71
|
+
* } else {
|
|
72
|
+
* // Handle unresolved contract
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* The validation process includes:
|
|
78
|
+
* - Finding a matching contract (self or service)
|
|
79
|
+
* - Validating dataschema URI and version if present
|
|
80
|
+
* - Validating event data against the contract schema
|
|
81
|
+
*/
|
|
82
|
+
validateInput(event: ArvoEvent): {
|
|
83
|
+
type: 'VALID';
|
|
84
|
+
} | {
|
|
85
|
+
type: 'CONTRACT_UNRESOLVED';
|
|
86
|
+
} | {
|
|
87
|
+
type: 'INVALID';
|
|
88
|
+
error: Error;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'INVALID_DATA';
|
|
91
|
+
error: z.ZodError;
|
|
92
|
+
};
|
|
93
|
+
}
|