Skip to content

Commit 407c97e

Browse files
committed
feat: allow actions in transitions triggered when a match is found
1 parent ddf1261 commit 407c97e

File tree

3 files changed

+115
-97
lines changed

3 files changed

+115
-97
lines changed

packages/automation/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ Transitions define how the machine moves between states. They can be triggered a
5151
- HTTP requests (polling)
5252
- Custom conditions
5353

54+
When a Transition gets new values, it uses its `check` function to determine if the values are a match or not.
55+
56+
Depending on the `check` result, it calls the `onMatch` or `onMismatch` function. Also, when there is a match, it can trigger actions and move the state machine to the next state.
57+
5458
### Listeners
5559

5660
Listeners monitor various events and feed data to transitions:

packages/automation/src/lib/state-machine.ts

Lines changed: 109 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import { State, StateParams } from './states';
2828
import { CheckFn, Transition } from './transitions';
2929
import {
30+
ActionDefinition,
3031
BaseStateMachineParams,
3132
ContextOrLiteral,
3233
StateDefinition,
@@ -196,102 +197,9 @@ export class StateMachine {
196197
debug: this.debug,
197198
};
198199

199-
const onEnterActions = [] as Action[];
200-
const onExitActions = [] as Action[];
201-
202-
const { actions = [] } = stateDefinition;
203-
204-
actions.forEach((action) => {
205-
switch (action.key) {
206-
case 'context':
207-
if (action.log?.path) {
208-
onEnterActions.push(
209-
new LogContextAction({
210-
debug: this.debug,
211-
stateMachine: this,
212-
path: action.log.path,
213-
})
214-
);
215-
}
216-
break;
217-
case 'litAction':
218-
onEnterActions.push(
219-
new LitActionAction({
220-
debug: this.debug,
221-
stateMachine: this,
222-
...action,
223-
})
224-
);
225-
break;
226-
case 'transaction':
227-
onEnterActions.push(
228-
new TransactionAction({
229-
debug: this.debug,
230-
stateMachine: this,
231-
...action,
232-
})
233-
);
234-
break;
235-
case 'useCapacityNFT':
236-
if ('capacityTokenId' in action) {
237-
this.context.set(
238-
'activeCapacityTokenId',
239-
this.resolveContextPathOrLiteral(action.capacityTokenId)
240-
);
241-
} else if ('mint' in action) {
242-
const mintCapacityCreditAction = new MintCapacityCreditAction({
243-
daysUntilUTCMidnightExpiration:
244-
action.daysUntilUTCMidnightExpiration,
245-
debug: this.debug,
246-
requestPerSecond: action.requestPerSecond,
247-
stateMachine: this,
248-
});
249-
onEnterActions.push(mintCapacityCreditAction);
250-
}
251-
if (this.debug) {
252-
const activeCapacityTokenId = this.context.get('activePkp');
253-
console.log(
254-
`Machine configured to use capacity token ${activeCapacityTokenId}`
255-
);
256-
}
257-
break;
258-
case 'usePkp':
259-
if ('pkp' in action) {
260-
this.context.set(
261-
'activePkp',
262-
this.resolveContextPathOrLiteral(action.pkp)
263-
);
264-
} else if ('mint' in action) {
265-
const mintPkpAction = new MintPkpAction({
266-
debug: this.debug,
267-
stateMachine: this,
268-
});
269-
onEnterActions.push(mintPkpAction);
270-
}
271-
if (this.debug) {
272-
const activePkp = this.context.get('activePkp');
273-
console.log(`Machine configured to use pkp ${activePkp}`);
274-
}
275-
break;
276-
default:
277-
throw new AutomationError(
278-
{
279-
info: {
280-
action,
281-
},
282-
},
283-
`Unknown action. Check error info.`
284-
);
285-
}
286-
});
287-
288200
// Merge all state actions
289-
stateParams.onEnter = async () => {
290-
await Promise.all(onEnterActions.map((action) => action.run()));
291-
};
292-
stateParams.onExit = async () => {
293-
await Promise.all(onExitActions.map((action) => action.run()));
294-
};
201+
const { actions = [] } = stateDefinition;
202+
stateParams.onEnter = this.mergeActions(actions);
295203

296204
this.addState(stateParams);
297205
}
@@ -301,6 +209,7 @@ export class StateMachine {
301209
* @param params The parameters for the transition.
302210
*/
303211
addTransition({
212+
actions = [],
304213
fromState,
305214
toState,
306215
listeners,
@@ -334,6 +243,7 @@ export class StateMachine {
334243
}
335244

336245
const transitioningOnMatch = async (values: (unknown | undefined)[]) => {
246+
await this.mergeActions(actions)();
337247
await onMatch?.(values);
338248
await this.transitionTo(toState);
339249
};
@@ -358,10 +268,11 @@ export class StateMachine {
358268
}
359269

360270
addTransitionFromDefinition(transitionDefinition: TransitionDefinition) {
361-
const { balances, evmContractEvent, fromState, timer, toState } =
271+
const { actions, balances, evmContractEvent, fromState, timer, toState } =
362272
transitionDefinition;
363273

364274
const transitionConfig: TransitionParams = {
275+
actions,
365276
fromState,
366277
toState,
367278
};
@@ -648,7 +559,108 @@ export class StateMachine {
648559
}
649560
}
650561

651-
private handleError(error: unknown, context: string) {
562+
/**
563+
* Merges the given action definitions into a single function that executes all actions concurrently.
564+
* @param actionDefinitions
565+
* @returns A function that executes all actions concurrently.
566+
* @private
567+
*/
568+
private mergeActions(
569+
actionDefinitions: ActionDefinition[]
570+
): voidAsyncFunction {
571+
const actions = [] as Action[];
572+
573+
actionDefinitions.forEach((action) => {
574+
switch (action.key) {
575+
case 'context':
576+
if (action.log?.path) {
577+
actions.push(
578+
new LogContextAction({
579+
debug: this.debug,
580+
stateMachine: this,
581+
path: action.log.path,
582+
})
583+
);
584+
}
585+
break;
586+
case 'litAction':
587+
actions.push(
588+
new LitActionAction({
589+
debug: this.debug,
590+
stateMachine: this,
591+
...action,
592+
})
593+
);
594+
break;
595+
case 'transaction':
596+
actions.push(
597+
new TransactionAction({
598+
debug: this.debug,
599+
stateMachine: this,
600+
...action,
601+
})
602+
);
603+
break;
604+
case 'useCapacityNFT':
605+
if ('capacityTokenId' in action) {
606+
this.context.set(
607+
'activeCapacityTokenId',
608+
this.resolveContextPathOrLiteral(action.capacityTokenId)
609+
);
610+
} else if ('mint' in action) {
611+
const mintCapacityCreditAction = new MintCapacityCreditAction({
612+
daysUntilUTCMidnightExpiration:
613+
action.daysUntilUTCMidnightExpiration,
614+
debug: this.debug,
615+
requestPerSecond: action.requestPerSecond,
616+
stateMachine: this,
617+
});
618+
actions.push(mintCapacityCreditAction);
619+
}
620+
if (this.debug) {
621+
const activeCapacityTokenId = this.context.get('activePkp');
622+
console.log(
623+
`Machine configured to use capacity token ${activeCapacityTokenId}`
624+
);
625+
}
626+
break;
627+
case 'usePkp':
628+
if ('pkp' in action) {
629+
this.context.set(
630+
'activePkp',
631+
this.resolveContextPathOrLiteral(action.pkp)
632+
);
633+
} else if ('mint' in action) {
634+
const mintPkpAction = new MintPkpAction({
635+
debug: this.debug,
636+
stateMachine: this,
637+
});
638+
actions.push(mintPkpAction);
639+
}
640+
if (this.debug) {
641+
const activePkp = this.context.get('activePkp');
642+
console.log(`Machine configured to use pkp ${activePkp}`);
643+
}
644+
break;
645+
default:
646+
throw new AutomationError(
647+
{
648+
info: {
649+
action,
650+
},
651+
},
652+
`Unknown action. Check error info.`
653+
);
654+
}
655+
});
656+
657+
return async () => {
658+
await Promise.all(actions.map((action) => action.run())).catch((err) => {
659+
this.handleError(err, `Error running actions. Check details.`);
660+
});
661+
};
662+
}
663+
652664
/**
653665
* Handles errors in the state machine.
654666
* @param error

packages/automation/src/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,13 @@ export interface TransitionDefinition {
150150
fromState: string;
151151
timer?: TimerTransitionDefinition;
152152
toState: string;
153+
actions?: ActionDefinition[];
153154
}
154155

155156
export interface TransitionParams
156157
extends Omit<BaseTransitionParams, 'onMatch'>,
157158
Partial<Pick<BaseTransitionParams, 'onMatch'>> {
159+
actions?: ActionDefinition[];
158160
fromState: string;
159161
toState: string;
160162
}

0 commit comments

Comments
 (0)