@ronaldroe/micro-flow 1.0.0
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/LICENSE +7 -0
- package/README.md +581 -0
- package/index.js +1 -0
- package/package.json +53 -0
- package/src/classes/base.js +143 -0
- package/src/classes/events/broadcast.js +57 -0
- package/src/classes/events/event.js +144 -0
- package/src/classes/events/index.js +4 -0
- package/src/classes/events/step_event.js +28 -0
- package/src/classes/events/workflow_event.js +28 -0
- package/src/classes/index.js +5 -0
- package/src/classes/state.js +197 -0
- package/src/classes/steps/conditional_step.js +80 -0
- package/src/classes/steps/flow_control_step.js +69 -0
- package/src/classes/steps/index.js +4 -0
- package/src/classes/steps/logic_step.js +102 -0
- package/src/classes/steps/step.js +111 -0
- package/src/classes/workflow.js +379 -0
- package/src/enums/base_types.js +6 -0
- package/src/enums/conditional_step_comparators.js +27 -0
- package/src/enums/delay_types.js +38 -0
- package/src/enums/errors.js +25 -0
- package/src/enums/flow_control_types.js +12 -0
- package/src/enums/index.js +13 -0
- package/src/enums/logic_step_types.js +16 -0
- package/src/enums/loop_types.js +12 -0
- package/src/enums/step_event_names.js +22 -0
- package/src/enums/step_statuses.js +17 -0
- package/src/enums/step_types.js +14 -0
- package/src/enums/sub_step_types.js +130 -0
- package/src/enums/workflow_event_names.js +26 -0
- package/src/enums/workflow_statuses.js +21 -0
- package/src/index.js +2 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LogicStep } from './index.js';
|
|
2
|
+
import flow_control_types from '../../enums/flow_control_types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FlowControlStep class for controlling workflow execution flow (break, continue, skip, pause).
|
|
6
|
+
* @class FlowControlStep
|
|
7
|
+
* @extends LogicStep
|
|
8
|
+
*/
|
|
9
|
+
export default class FlowControlStep extends LogicStep {
|
|
10
|
+
static step_name = 'flow_control';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new FlowControlStep instance.
|
|
14
|
+
* @param {Object} options - Configuration options.
|
|
15
|
+
* @param {Object} [options.conditional] - Conditional configuration.
|
|
16
|
+
* @param {*} [options.conditional.subject] - Subject to evaluate.
|
|
17
|
+
* @param {string} [options.conditional.operator] - Comparison operator.
|
|
18
|
+
* @param {*} [options.conditional.value] - Value to compare against.
|
|
19
|
+
* @param {string} [options.name] - Name of the step.
|
|
20
|
+
* @param {string} [options.flow_control_type=flow_control_types.BREAK] - Type of flow control.
|
|
21
|
+
* @throws {Error} Throws if flow_control_type is invalid.
|
|
22
|
+
*/
|
|
23
|
+
constructor({
|
|
24
|
+
conditional = {
|
|
25
|
+
subject: null,
|
|
26
|
+
operator: null,
|
|
27
|
+
value: null,
|
|
28
|
+
},
|
|
29
|
+
name,
|
|
30
|
+
flow_control_type = flow_control_types.BREAK,
|
|
31
|
+
}) {
|
|
32
|
+
super({
|
|
33
|
+
name,
|
|
34
|
+
conditional
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!Object.values(flow_control_types).includes(flow_control_type)) {
|
|
38
|
+
throw new Error(`Invalid flow control type: ${flow_control_type}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.flow_control_type = flow_control_type;
|
|
42
|
+
this.callable = this.shouldFlowControl.bind(this);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Evaluates the condition and sets the appropriate flow control flag.
|
|
47
|
+
* @async
|
|
48
|
+
* @returns {Promise<boolean>} True if the flow control should be activated.
|
|
49
|
+
*/
|
|
50
|
+
async shouldFlowControl() {
|
|
51
|
+
if (this.checkCondition()) {
|
|
52
|
+
this.log(
|
|
53
|
+
this.getState('events.step.event_names.CONDITIONAL_TRUE_BRANCH_EXECUTED'),
|
|
54
|
+
`Break condition met for step: ${this.name}`
|
|
55
|
+
);
|
|
56
|
+
this.setState(`should_${this.flow_control_type}`, true);
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
} else {
|
|
60
|
+
this.log(
|
|
61
|
+
this.getState('events.step.event_names.CONDITIONAL_FALSE_BRANCH_EXECUTED'),
|
|
62
|
+
`Break condition not met for step: ${this.name}`
|
|
63
|
+
);
|
|
64
|
+
this.setState(`should_${this.flow_control_type}`, false);
|
|
65
|
+
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import Step from './step.js';
|
|
2
|
+
import { step_types } from '../../enums/index.js';
|
|
3
|
+
|
|
4
|
+
const conditional_keys = [
|
|
5
|
+
'subject',
|
|
6
|
+
'operator',
|
|
7
|
+
'value',
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* LogicStep class for conditional logic operations.
|
|
12
|
+
* @class LogicStep
|
|
13
|
+
* @extends Step
|
|
14
|
+
*/
|
|
15
|
+
export default class LogicStep extends Step {
|
|
16
|
+
static step_name = 'logic';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new LogicStep instance.
|
|
20
|
+
* @param {Object} options - Configuration options.
|
|
21
|
+
* @param {string} [options.name] - Name of the step.
|
|
22
|
+
* @param {Object} [options.conditional] - Conditional configuration.
|
|
23
|
+
* @param {*} [options.conditional.subject] - Subject to evaluate.
|
|
24
|
+
* @param {string} [options.conditional.operator] - Comparison operator.
|
|
25
|
+
* @param {*} [options.conditional.value] - Value to compare against.
|
|
26
|
+
* @param {Function} [options.callable=async () => {}] - Function to execute.
|
|
27
|
+
*/
|
|
28
|
+
constructor({
|
|
29
|
+
name,
|
|
30
|
+
conditional = {
|
|
31
|
+
subject: null,
|
|
32
|
+
operator: null,
|
|
33
|
+
value: null,
|
|
34
|
+
},
|
|
35
|
+
callable = async () => {},
|
|
36
|
+
}) {
|
|
37
|
+
super({
|
|
38
|
+
name,
|
|
39
|
+
base_type: step_types.LOGIC_STEP,
|
|
40
|
+
callable
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
this.validateAndSetConditional(conditional);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Evaluates the conditional expression.
|
|
48
|
+
* @returns {boolean} True if the condition is met.
|
|
49
|
+
* @throws {Error} Throws if operator is unknown.
|
|
50
|
+
*/
|
|
51
|
+
checkCondition() {
|
|
52
|
+
const subject = this.subject;
|
|
53
|
+
const operator = this.operator;
|
|
54
|
+
const value = this.value;
|
|
55
|
+
|
|
56
|
+
switch (operator) {
|
|
57
|
+
case this.getState('conditional_step_comparators.STRICT_EQUALS'):
|
|
58
|
+
case this.getState('conditional_step_comparators.SIGN_STRICT_EQUALS'):
|
|
59
|
+
return subject === value;
|
|
60
|
+
case this.getState('conditional_step_comparators.SIGN_EQUALS'):
|
|
61
|
+
case this.getState('conditional_step_comparators.EQUALS'):
|
|
62
|
+
return subject == value;
|
|
63
|
+
case this.getState('conditional_step_comparators.NOT_EQUALS'):
|
|
64
|
+
case this.getState('conditional_step_comparators.SIGN_NOT_EQUALS'):
|
|
65
|
+
return subject != value;
|
|
66
|
+
case this.getState('conditional_step_comparators.STRICT_NOT_EQUALS'):
|
|
67
|
+
case this.getState('conditional_step_comparators.SIGN_STRICT_NOT_EQUALS'):
|
|
68
|
+
return subject !== value;
|
|
69
|
+
case this.getState('conditional_step_comparators.GREATER_THAN'):
|
|
70
|
+
case this.getState('conditional_step_comparators.SIGN_GREATER_THAN'):
|
|
71
|
+
return subject > value;
|
|
72
|
+
case this.getState('conditional_step_comparators.LESS_THAN'):
|
|
73
|
+
case this.getState('conditional_step_comparators.SIGN_LESS_THAN'):
|
|
74
|
+
return subject < value;
|
|
75
|
+
case this.getState('conditional_step_comparators.GREATER_THAN_OR_EQUAL'):
|
|
76
|
+
case this.getState('conditional_step_comparators.SIGN_GREATER_THAN_OR_EQUAL'):
|
|
77
|
+
return subject >= value;
|
|
78
|
+
case this.getState('conditional_step_comparators.LESS_THAN_OR_EQUAL'):
|
|
79
|
+
case this.getState('conditional_step_comparators.SIGN_LESS_THAN_OR_EQUAL'):
|
|
80
|
+
return subject <= value;
|
|
81
|
+
default:
|
|
82
|
+
throw new Error(`Unknown operator: ${operator}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validates and sets the conditional properties.
|
|
88
|
+
* @param {Object} conditional - Conditional configuration object.
|
|
89
|
+
* @throws {Error} Throws if conditional is invalid.
|
|
90
|
+
*/
|
|
91
|
+
validateAndSetConditional(conditional) {
|
|
92
|
+
for (const [key, value] of Object.entries(conditional)) {
|
|
93
|
+
if (!value || !Object.values(conditional_keys).includes(key)) {
|
|
94
|
+
throw new Error(this.getState('messages.errors.INVALID_CONDITIONAL'));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.subject = conditional.subject;
|
|
99
|
+
this.operator = conditional.operator;
|
|
100
|
+
this.value = conditional.value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import Base from '../base.js';
|
|
2
|
+
import Workflow from '../workflow.js';
|
|
3
|
+
import { base_types, step_types } from '../../enums/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Step class representing an executable unit within a workflow.
|
|
7
|
+
* @class Step
|
|
8
|
+
* @extends Base
|
|
9
|
+
*/
|
|
10
|
+
export default class Step extends Base {
|
|
11
|
+
static step_name = 'step';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new Step instance.
|
|
15
|
+
* @param {Object} options - Configuration options.
|
|
16
|
+
* @param {string} [options.name] - Name of the step.
|
|
17
|
+
* @param {string} [options.step_type=step_types.ACTION] - Type of the step.
|
|
18
|
+
* @param {Function|Step|Workflow} [options.callable=async () => {}] - Function, Step, or Workflow to execute.
|
|
19
|
+
* @param {string} [options.sub_step_type=null] - Sub-type of the step.
|
|
20
|
+
*/
|
|
21
|
+
constructor({
|
|
22
|
+
name,
|
|
23
|
+
step_type = step_types.ACTION,
|
|
24
|
+
callable = async () => {},
|
|
25
|
+
sub_step_type = null,
|
|
26
|
+
}) {
|
|
27
|
+
super({ name, base_type: base_types.STEP });
|
|
28
|
+
|
|
29
|
+
this.callable = callable;
|
|
30
|
+
|
|
31
|
+
// Store off the original callable object, because if it's a Step or Workflow,
|
|
32
|
+
// this.callable is set to the execute method of that object, but we may need to access its properties later.
|
|
33
|
+
this.callable_object = callable;
|
|
34
|
+
|
|
35
|
+
this.step_type = step_type;
|
|
36
|
+
this.sub_step_type = sub_step_type;
|
|
37
|
+
|
|
38
|
+
this.errors = [];
|
|
39
|
+
this.result = null;
|
|
40
|
+
this.retry_results = [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Executes the step's callable function, Step, or Workflow.
|
|
45
|
+
* @async
|
|
46
|
+
* @returns {Promise<Step>} The step instance with execution results.
|
|
47
|
+
*/
|
|
48
|
+
async execute() {
|
|
49
|
+
this.markAsRunning();
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
this.result = await this._callable();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
this.errors.push(error);
|
|
55
|
+
this.markAsFailed();
|
|
56
|
+
|
|
57
|
+
this.timing.end_time = new Date();
|
|
58
|
+
this.timing.execution_time_ms = this.timing.end_time - this.timing.start_time;
|
|
59
|
+
|
|
60
|
+
if (this.getState('exit_on_error')) {
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.status !== this.getState('statuses')[this.base_type].FAILED) {
|
|
66
|
+
this.markAsComplete();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (['step', 'workflow'].includes(this.callable_type)) {
|
|
70
|
+
this.callable_object.prepareReturnData();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Determines the type of the callable (function, step, or workflow).
|
|
78
|
+
* @param {Function|Step|Workflow} callable - The callable to check.
|
|
79
|
+
* @returns {string} The type: 'function', 'step', or 'workflow'.
|
|
80
|
+
* @throws {Error} Throws if callable type is invalid.
|
|
81
|
+
*/
|
|
82
|
+
getCallableType(callable) {
|
|
83
|
+
if (callable instanceof Workflow) {
|
|
84
|
+
return 'workflow';
|
|
85
|
+
} else if (callable instanceof Step) {
|
|
86
|
+
return 'step';
|
|
87
|
+
} else if (typeof callable === 'function') {
|
|
88
|
+
return 'function';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
throw new Error('Invalid callable type. Must be one of function, Step, or Workflow.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Sets the callable for the step and determines its type.
|
|
96
|
+
* @param {Function|Step|Workflow} callable - The callable to set.
|
|
97
|
+
*/
|
|
98
|
+
set callable(callable) {
|
|
99
|
+
this.callable_type = this.getCallableType(callable);
|
|
100
|
+
|
|
101
|
+
if (['step', 'workflow'].includes(this.callable_type)) {
|
|
102
|
+
if (this.callable_type === 'step') {
|
|
103
|
+
callable.parentWorkflowId = this.parentWorkflowId ?? null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this._callable = callable.execute.bind(callable);
|
|
107
|
+
} else {
|
|
108
|
+
this._callable = callable;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { Base } from './index.js';
|
|
2
|
+
import { base_types } from '../enums/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Workflow class for managing and executing a sequence of steps.
|
|
6
|
+
* @class Workflow
|
|
7
|
+
* @extends Base
|
|
8
|
+
*/
|
|
9
|
+
export default class Workflow extends Base {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new Workflow instance.
|
|
12
|
+
* @param {Object} options - Configuration options.
|
|
13
|
+
* @param {string} [options.name] - Name of the workflow.
|
|
14
|
+
* @param {boolean} [options.exit_on_error=false] - Whether to exit on error.
|
|
15
|
+
* @param {Array} [options.steps=[]] - Array of steps to add to the workflow.
|
|
16
|
+
* @param {boolean} [options.throw_on_empty=false] - Whether to throw error if workflow is empty.
|
|
17
|
+
*/
|
|
18
|
+
constructor({
|
|
19
|
+
name,
|
|
20
|
+
exit_on_error = false,
|
|
21
|
+
steps = [],
|
|
22
|
+
throw_on_empty = false
|
|
23
|
+
}) {
|
|
24
|
+
super({ name, base_type: base_types.WORKFLOW });
|
|
25
|
+
|
|
26
|
+
this.initializeWorkflowState();
|
|
27
|
+
|
|
28
|
+
this.addSteps(steps);
|
|
29
|
+
|
|
30
|
+
this.exit_on_error = exit_on_error;
|
|
31
|
+
this.throw_on_empty = throw_on_empty;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Executes the workflow by running all steps in sequence.
|
|
36
|
+
* @async
|
|
37
|
+
* @returns {Promise<Workflow>} The workflow instance with execution results.
|
|
38
|
+
* @throws {Error} Throws if workflow is empty and throw_on_empty is true.
|
|
39
|
+
*/
|
|
40
|
+
async execute() {
|
|
41
|
+
if (this.isEmpty()) {
|
|
42
|
+
if (this.throw_on_empty) {
|
|
43
|
+
throw new Error('Cannot execute an empty workflow');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.markAsComplete();
|
|
47
|
+
this.prepareResult('Workflow is empty', null);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.markAsRunning();
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < this._steps.length; i++) {
|
|
54
|
+
if (this.getState('should_pause')) {
|
|
55
|
+
this.markAsPaused();
|
|
56
|
+
this.setState('should_pause', false);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this.getState('should_break')) {
|
|
61
|
+
this.log('workflow_break_executed', `Workflow "${this.name}" execution broken at step ${this._steps[i].name} - ${this._steps[i].id}.`);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.getState('should_skip')) {
|
|
66
|
+
this.log(
|
|
67
|
+
this.getState('events.workflow.event_names.WORKFLOW_STEP_SKIPPED'),
|
|
68
|
+
`Workflow "${this.name}" skipping step ${this._steps[i].name} - ${this._steps[i].id}.`
|
|
69
|
+
);
|
|
70
|
+
this.setState('should_skip', false);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.current_step = this._steps[i].id;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const step_result = await this.step();
|
|
78
|
+
this.prepareResult('Success', step_result);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this.markAsFailed();
|
|
81
|
+
this.prepareResult(`Workflow execution failed at step ${this.steps_by_id[this.current_step].name} - ${this.current_step}`, { error });
|
|
82
|
+
|
|
83
|
+
if (this.exit_on_error) {
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.markAsComplete();
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resumes a paused workflow.
|
|
95
|
+
* @async
|
|
96
|
+
* @returns {Promise<Workflow>} The workflow instance.
|
|
97
|
+
*/
|
|
98
|
+
async resume() {
|
|
99
|
+
this.should_pause = false;
|
|
100
|
+
this.timing.resume_time = new Date();
|
|
101
|
+
|
|
102
|
+
this.getState('events.workflow').emit(
|
|
103
|
+
this.getState('event_names.workflow').WORKFLOW_RESUMED,
|
|
104
|
+
this.getState()
|
|
105
|
+
);
|
|
106
|
+
return this.execute();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Executes a single step in the workflow.
|
|
111
|
+
* @async
|
|
112
|
+
* @returns {Promise<*>} The result of the step execution.
|
|
113
|
+
*/
|
|
114
|
+
async step() {
|
|
115
|
+
const step = this.steps_by_id[this.current_step];
|
|
116
|
+
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = await step.execute();
|
|
120
|
+
this.results.push(result);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (this.exit_on_error) {
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Adds a step to the workflow.
|
|
132
|
+
* @param {Step} step - The step to add.
|
|
133
|
+
* @throws {Error} Throws if step is not a valid Step instance.
|
|
134
|
+
*/
|
|
135
|
+
addStep(step) {
|
|
136
|
+
if (typeof step.getCallableType !== 'function') {
|
|
137
|
+
throw new Error('Invalid step type. Must be an instance of Step.');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!Array.isArray(this._steps)) {
|
|
141
|
+
this._steps = [];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!this.steps_by_id || typeof this.steps_by_id !== 'object') {
|
|
145
|
+
this.steps_by_id = {};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.steps_by_id[step.id] = step;
|
|
149
|
+
|
|
150
|
+
step.parentWorkflowId = this.id;
|
|
151
|
+
this._steps.push(step);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Adds a step at a specific index in the workflow.
|
|
156
|
+
* @param {Step} step - The step to add.
|
|
157
|
+
* @param {number} index - The index at which to insert the step.
|
|
158
|
+
*/
|
|
159
|
+
addStepAtIndex(step, index) {
|
|
160
|
+
step.parentWorkflowId = this.id;
|
|
161
|
+
this._steps.splice(index, 0, step);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Adds multiple steps to the workflow.
|
|
166
|
+
* @param {Step[]} steps - Array of steps to add.
|
|
167
|
+
*/
|
|
168
|
+
addSteps(steps) {
|
|
169
|
+
steps.forEach(step => this.addStep(step));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Clears all steps from the workflow.
|
|
174
|
+
*/
|
|
175
|
+
clearSteps() {
|
|
176
|
+
this._steps = [];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Deletes a step from the workflow by its ID.
|
|
181
|
+
* @param {string} stepId - The ID of the step to delete.
|
|
182
|
+
*/
|
|
183
|
+
deleteStep(stepId) {
|
|
184
|
+
this._steps = this._steps.filter(step => step.id !== stepId);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Deletes a step from the workflow by its index.
|
|
189
|
+
* @param {number} index - The index of the step to delete.
|
|
190
|
+
*/
|
|
191
|
+
deleteStepByIndex(index) {
|
|
192
|
+
this._steps.splice(index, 1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Initializes the workflow state with default values.
|
|
197
|
+
*/
|
|
198
|
+
initializeWorkflowState() {
|
|
199
|
+
this.results = [];
|
|
200
|
+
this.exit_on_error = false;
|
|
201
|
+
this.current_step = null;
|
|
202
|
+
this.should_break = false;
|
|
203
|
+
this.should_continue = false;
|
|
204
|
+
this.should_pause = false;
|
|
205
|
+
this.should_skip = false;
|
|
206
|
+
this.status = this.getState('statuses.workflow').CREATED;
|
|
207
|
+
this._steps = [];
|
|
208
|
+
this.throw_on_empty = this.throw_on_empty;
|
|
209
|
+
this.timing = {
|
|
210
|
+
...this.timing,
|
|
211
|
+
create_time: new Date(),
|
|
212
|
+
pause_time: null,
|
|
213
|
+
resume_time: null,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const workflows = this.getState('workflows');
|
|
217
|
+
workflows[this.id] = this;
|
|
218
|
+
this.setState('workflows', workflows);
|
|
219
|
+
|
|
220
|
+
this.log(
|
|
221
|
+
this.getState('event_names.workflow').WORKFLOW_CREATED,
|
|
222
|
+
`Workflow "${this.name}" initialized.`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Checks if the workflow has no steps.
|
|
228
|
+
* @returns {boolean} True if the workflow is empty.
|
|
229
|
+
*/
|
|
230
|
+
isEmpty() {
|
|
231
|
+
return !this._steps && !this._steps.length
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Marks the workflow as created.
|
|
236
|
+
* @returns {string} The CREATED status.
|
|
237
|
+
*/
|
|
238
|
+
markAsCreated() {
|
|
239
|
+
this.timing.create_time = new Date();
|
|
240
|
+
|
|
241
|
+
this.log(
|
|
242
|
+
this.getState('event_names.workflow').WORKFLOW_CREATED,
|
|
243
|
+
`Workflow "${this.name}" created.`
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return this.getState('workflow.statuses').CREATED;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Marks the workflow as paused.
|
|
251
|
+
*/
|
|
252
|
+
markAsPaused() {
|
|
253
|
+
this.timing.pause_time = new Date();
|
|
254
|
+
this.status = this.getState('workflow.statuses').PAUSED;
|
|
255
|
+
|
|
256
|
+
this.getState('events.workflow').emit(
|
|
257
|
+
this.getState('event_names.workflow').WORKFLOW_PAUSED,
|
|
258
|
+
this.getState()
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Marks the workflow as resumed.
|
|
264
|
+
*/
|
|
265
|
+
markAsResumed() {
|
|
266
|
+
this.timing.resume_time = new Date();
|
|
267
|
+
this.status = this.getState('workflow.statuses').RUNNING;
|
|
268
|
+
|
|
269
|
+
this.getState('events.workflow').emit(
|
|
270
|
+
this.getState('event_names.workflow').WORKFLOW_RESUMED,
|
|
271
|
+
this.getState()
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Moves a step from one index to another.
|
|
277
|
+
* @param {number} fromIndex - The current index of the step.
|
|
278
|
+
* @param {number} toIndex - The target index for the step.
|
|
279
|
+
*/
|
|
280
|
+
moveStep(fromIndex, toIndex) {
|
|
281
|
+
const [step] = this._steps.splice(fromIndex, 1);
|
|
282
|
+
this._steps.splice(toIndex, 0, step);
|
|
283
|
+
|
|
284
|
+
this.getState('events.workflow').emit(
|
|
285
|
+
this.getState('event_names.workflow').WORKFLOW_STEP_MOVED,
|
|
286
|
+
this.getState()
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Pauses the workflow execution.
|
|
292
|
+
*/
|
|
293
|
+
pause() {
|
|
294
|
+
this.should_pause = true;
|
|
295
|
+
this.timing.pause_time = new Date();
|
|
296
|
+
|
|
297
|
+
this.getState('events.workflow').emit(
|
|
298
|
+
this.getState('event_names.workflow').WORKFLOW_PAUSED,
|
|
299
|
+
this.getState()
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Removes and returns the last step from the workflow.
|
|
305
|
+
* @returns {Step} The last step.
|
|
306
|
+
*/
|
|
307
|
+
popStep() {
|
|
308
|
+
return this._steps.pop();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Prepares a result object and adds it to the results array.
|
|
313
|
+
* @param {string} message - Result message.
|
|
314
|
+
* @param {*} data - Result data.
|
|
315
|
+
*/
|
|
316
|
+
prepareResult(message, data) {
|
|
317
|
+
this.results.push({ message, data });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Adds a step to the end of the workflow.
|
|
322
|
+
* @param {Step} step - The step to add.
|
|
323
|
+
*/
|
|
324
|
+
pushStep(step) {
|
|
325
|
+
this.addStep(step);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Adds multiple steps to the end of the workflow.
|
|
330
|
+
* @param {Step[]} steps - Array of steps to add.
|
|
331
|
+
*/
|
|
332
|
+
pushSteps(steps) {
|
|
333
|
+
steps.forEach(step => this.addStep(step));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Removes and returns the first step from the workflow.
|
|
338
|
+
* @returns {Step} The first step.
|
|
339
|
+
*/
|
|
340
|
+
shiftStep() {
|
|
341
|
+
return this._steps.shift();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Adds a step to the beginning of the workflow.
|
|
346
|
+
* @param {Step} step - The step to add.
|
|
347
|
+
* @throws {Error} Throws if step is not a valid Step instance.
|
|
348
|
+
*/
|
|
349
|
+
unshiftStep(step) {
|
|
350
|
+
if (typeof step.getCallableType !== 'function') {
|
|
351
|
+
throw new Error('Invalid step type. Must be an instance of Step.');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!this.steps_by_id || typeof this.steps_by_id !== 'object') {
|
|
355
|
+
this.steps_by_id = {};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
this.steps_by_id[step.id] = step;
|
|
359
|
+
|
|
360
|
+
step.parentWorkflowId = this.id;
|
|
361
|
+
this._steps.unshift(step);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Gets the array of steps in the workflow.
|
|
366
|
+
* @returns {Step[]} Array of steps.
|
|
367
|
+
*/
|
|
368
|
+
get steps() {
|
|
369
|
+
return this._steps;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Sets the steps array by adding multiple steps.
|
|
374
|
+
* @param {Step[]} steps - Array of steps to add.
|
|
375
|
+
*/
|
|
376
|
+
set steps(steps) {
|
|
377
|
+
this.addSteps(steps);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enumeration of comparison operators for conditional steps.
|
|
3
|
+
* Provides both named and symbolic operator formats.
|
|
4
|
+
*
|
|
5
|
+
* @enum {string}
|
|
6
|
+
* @readonly
|
|
7
|
+
*/
|
|
8
|
+
const conditional_step_comparators = {
|
|
9
|
+
EQUALS: 'equals',
|
|
10
|
+
STRICT_EQUALS: 'strict_equals',
|
|
11
|
+
NOT_EQUALS: 'not_equals',
|
|
12
|
+
STRICT_NOT_EQUALS: 'strict_not_equals',
|
|
13
|
+
GREATER_THAN: 'greater_than',
|
|
14
|
+
LESS_THAN: 'less_than',
|
|
15
|
+
GREATER_THAN_OR_EQUAL: 'greater_than_or_equal',
|
|
16
|
+
LESS_THAN_OR_EQUAL: 'less_than_or_equal',
|
|
17
|
+
SIGN_EQUALS: '==',
|
|
18
|
+
SIGN_STRICT_EQUALS: '===',
|
|
19
|
+
SIGN_NOT_EQUALS: '!=',
|
|
20
|
+
SIGN_STRICT_NOT_EQUALS: '!==',
|
|
21
|
+
SIGN_GREATER_THAN: '>',
|
|
22
|
+
SIGN_LESS_THAN: '<',
|
|
23
|
+
SIGN_GREATER_THAN_OR_EQUAL: '>=',
|
|
24
|
+
SIGN_LESS_THAN_OR_EQUAL: '<=',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default conditional_step_comparators;
|