@ronaldroe/micro-flow 0.0.9

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.
Files changed (110) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +527 -0
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +7 -0
  5. package/dist/src/classes/base.js +4 -0
  6. package/dist/src/classes/base.js.map +7 -0
  7. package/dist/src/classes/events/broadcast.js +2 -0
  8. package/dist/src/classes/events/broadcast.js.map +7 -0
  9. package/dist/src/classes/events/event.js +2 -0
  10. package/dist/src/classes/events/event.js.map +7 -0
  11. package/dist/src/classes/events/index.js +2 -0
  12. package/dist/src/classes/events/index.js.map +7 -0
  13. package/dist/src/classes/events/state_event.js +2 -0
  14. package/dist/src/classes/events/state_event.js.map +7 -0
  15. package/dist/src/classes/events/step_event.js +2 -0
  16. package/dist/src/classes/events/step_event.js.map +7 -0
  17. package/dist/src/classes/events/workflow_event.js +2 -0
  18. package/dist/src/classes/events/workflow_event.js.map +7 -0
  19. package/dist/src/classes/index.js +2 -0
  20. package/dist/src/classes/index.js.map +7 -0
  21. package/dist/src/classes/state.js +2 -0
  22. package/dist/src/classes/state.js.map +7 -0
  23. package/dist/src/classes/steps/case.js +2 -0
  24. package/dist/src/classes/steps/case.js.map +7 -0
  25. package/dist/src/classes/steps/conditional_step.js +2 -0
  26. package/dist/src/classes/steps/conditional_step.js.map +7 -0
  27. package/dist/src/classes/steps/delay_step.js +2 -0
  28. package/dist/src/classes/steps/delay_step.js.map +7 -0
  29. package/dist/src/classes/steps/flow_control_step.js +2 -0
  30. package/dist/src/classes/steps/flow_control_step.js.map +7 -0
  31. package/dist/src/classes/steps/index.js +2 -0
  32. package/dist/src/classes/steps/index.js.map +7 -0
  33. package/dist/src/classes/steps/logic_step.js +2 -0
  34. package/dist/src/classes/steps/logic_step.js.map +7 -0
  35. package/dist/src/classes/steps/loop_step.js +2 -0
  36. package/dist/src/classes/steps/loop_step.js.map +7 -0
  37. package/dist/src/classes/steps/step.js +2 -0
  38. package/dist/src/classes/steps/step.js.map +7 -0
  39. package/dist/src/classes/steps/switch_step.js +2 -0
  40. package/dist/src/classes/steps/switch_step.js.map +7 -0
  41. package/dist/src/classes/workflow.js +2 -0
  42. package/dist/src/classes/workflow.js.map +7 -0
  43. package/dist/src/classes/workflow.test.js +2 -0
  44. package/dist/src/classes/workflow.test.js.map +7 -0
  45. package/dist/src/enums/base_types.js +2 -0
  46. package/dist/src/enums/base_types.js.map +7 -0
  47. package/dist/src/enums/conditional_step_comparators.js +2 -0
  48. package/dist/src/enums/conditional_step_comparators.js.map +7 -0
  49. package/dist/src/enums/delay_types.js +2 -0
  50. package/dist/src/enums/delay_types.js.map +7 -0
  51. package/dist/src/enums/errors.js +6 -0
  52. package/dist/src/enums/errors.js.map +7 -0
  53. package/dist/src/enums/flow_control_types.js +2 -0
  54. package/dist/src/enums/flow_control_types.js.map +7 -0
  55. package/dist/src/enums/index.js +2 -0
  56. package/dist/src/enums/index.js.map +7 -0
  57. package/dist/src/enums/logic_step_types.js +2 -0
  58. package/dist/src/enums/logic_step_types.js.map +7 -0
  59. package/dist/src/enums/loop_types.js +2 -0
  60. package/dist/src/enums/loop_types.js.map +7 -0
  61. package/dist/src/enums/state_event_names.js +2 -0
  62. package/dist/src/enums/state_event_names.js.map +7 -0
  63. package/dist/src/enums/step_event_names.js +2 -0
  64. package/dist/src/enums/step_event_names.js.map +7 -0
  65. package/dist/src/enums/step_statuses.js +2 -0
  66. package/dist/src/enums/step_statuses.js.map +7 -0
  67. package/dist/src/enums/step_types.js +2 -0
  68. package/dist/src/enums/step_types.js.map +7 -0
  69. package/dist/src/enums/sub_step_types.js +2 -0
  70. package/dist/src/enums/sub_step_types.js.map +7 -0
  71. package/dist/src/enums/workflow_event_names.js +2 -0
  72. package/dist/src/enums/workflow_event_names.js.map +7 -0
  73. package/dist/src/enums/workflow_statuses.js +2 -0
  74. package/dist/src/enums/workflow_statuses.js.map +7 -0
  75. package/package.json +58 -0
  76. package/src/classes/base.js +143 -0
  77. package/src/classes/events/broadcast.js +57 -0
  78. package/src/classes/events/event.js +144 -0
  79. package/src/classes/events/index.js +5 -0
  80. package/src/classes/events/state_event.js +28 -0
  81. package/src/classes/events/step_event.js +28 -0
  82. package/src/classes/events/workflow_event.js +28 -0
  83. package/src/classes/index.js +5 -0
  84. package/src/classes/state.js +301 -0
  85. package/src/classes/steps/case.js +70 -0
  86. package/src/classes/steps/conditional_step.js +90 -0
  87. package/src/classes/steps/delay_step.js +103 -0
  88. package/src/classes/steps/flow_control_step.js +70 -0
  89. package/src/classes/steps/index.js +8 -0
  90. package/src/classes/steps/logic_step.js +148 -0
  91. package/src/classes/steps/loop_step.js +147 -0
  92. package/src/classes/steps/step.js +129 -0
  93. package/src/classes/steps/switch_step.js +64 -0
  94. package/src/classes/workflow.js +383 -0
  95. package/src/enums/base_types.js +6 -0
  96. package/src/enums/conditional_step_comparators.js +57 -0
  97. package/src/enums/delay_types.js +31 -0
  98. package/src/enums/errors.js +27 -0
  99. package/src/enums/flow_control_types.js +12 -0
  100. package/src/enums/index.js +14 -0
  101. package/src/enums/logic_step_types.js +16 -0
  102. package/src/enums/loop_types.js +14 -0
  103. package/src/enums/state_event_names.js +17 -0
  104. package/src/enums/step_event_names.js +25 -0
  105. package/src/enums/step_statuses.js +17 -0
  106. package/src/enums/step_types.js +15 -0
  107. package/src/enums/sub_step_types.js +20 -0
  108. package/src/enums/workflow_event_names.js +27 -0
  109. package/src/enums/workflow_statuses.js +21 -0
  110. package/src/index.js +2 -0
@@ -0,0 +1,148 @@
1
+ import Step from './step.js';
2
+ import { conditional_step_comparators, step_types } from '../../enums/index.js';
3
+
4
+ /**
5
+ * LogicStep class for conditional logic operations.
6
+ * @class LogicStep
7
+ * @extends Step
8
+ */
9
+ export default class LogicStep extends Step {
10
+ static step_name = 'logic';
11
+
12
+ /**
13
+ * Creates a new LogicStep instance.
14
+ * @param {Object} options - Configuration options.
15
+ * @param {string} [options.name] - Name of the step.
16
+ * @param {Object} [options.conditional] - Conditional configuration.
17
+ * @param {*} [options.conditional.subject] - Subject to evaluate.
18
+ * @param {conditional_step_comparators|string} [options.conditional.operator] - Comparison operator.
19
+ * @param {*} [options.conditional.value] - Value to compare against.
20
+ * @param {Function} [options.callable=async () => {}] - Function to execute.
21
+ */
22
+ constructor({
23
+ name,
24
+ callable = async () => {},
25
+ conditional = {
26
+ operator: null,
27
+ subject: null,
28
+ value: null,
29
+ },
30
+ }) {
31
+ super({
32
+ name,
33
+ base_type: step_types.LOGIC,
34
+ callable
35
+ });
36
+
37
+ this.setConditional(conditional);
38
+ }
39
+
40
+ /**
41
+ * Evaluates the conditional expression.
42
+ * @returns {boolean} True if the condition is met.
43
+ * @throws {Error} Throws if operator is unknown.
44
+ */
45
+ checkCondition() {
46
+ const subject = this.conditional.subject;
47
+ const operator = this.conditional.operator;
48
+ const value = this.conditional.value;
49
+
50
+ switch (operator) {
51
+ case this.getState('conditional_step_comparators.STRICT_EQUALS'):
52
+ case this.getState('conditional_step_comparators.SIGN_STRICT_EQUALS'):
53
+ return subject === value;
54
+ case this.getState('conditional_step_comparators.SIGN_EQUALS'):
55
+ case this.getState('conditional_step_comparators.EQUALS'):
56
+ return subject == value;
57
+ case this.getState('conditional_step_comparators.NOT_EQUALS'):
58
+ case this.getState('conditional_step_comparators.SIGN_NOT_EQUALS'):
59
+ return subject != value;
60
+ case this.getState('conditional_step_comparators.STRICT_NOT_EQUALS'):
61
+ case this.getState('conditional_step_comparators.SIGN_STRICT_NOT_EQUALS'):
62
+ return subject !== value;
63
+ case this.getState('conditional_step_comparators.GREATER_THAN'):
64
+ case this.getState('conditional_step_comparators.SIGN_GREATER_THAN'):
65
+ return subject > value;
66
+ case this.getState('conditional_step_comparators.LESS_THAN'):
67
+ case this.getState('conditional_step_comparators.SIGN_LESS_THAN'):
68
+ return subject < value;
69
+ case this.getState('conditional_step_comparators.GREATER_THAN_OR_EQUAL'):
70
+ case this.getState('conditional_step_comparators.SIGN_GREATER_THAN_OR_EQUAL'):
71
+ return subject >= value;
72
+ case this.getState('conditional_step_comparators.LESS_THAN_OR_EQUAL'):
73
+ case this.getState('conditional_step_comparators.SIGN_LESS_THAN_OR_EQUAL'):
74
+ return subject <= value;
75
+ case this.getState('conditional_step_comparators.STRING_CONTAINS'):
76
+ case this.getState('conditional_step_comparators.STRING_INCLUDES'):
77
+ case this.getState('conditional_step_comparators.ARRAY_CONTAINS'):
78
+ case this.getState('conditional_step_comparators.ARRAY_INCLUDES'):
79
+ case this.getState('conditional_step_comparators.IN'):
80
+ return (Array.isArray(subject) || typeof subject === 'string') && subject.includes(value);
81
+ case this.getState('conditional_step_comparators.STRING_NOT_CONTAINS'):
82
+ case this.getState('conditional_step_comparators.STRING_NOT_INCLUDES'):
83
+ case this.getState('conditional_step_comparators.ARRAY_NOT_CONTAINS'):
84
+ case this.getState('conditional_step_comparators.ARRAY_NOT_INCLUDES'):
85
+ case this.getState('conditional_step_comparators.NOT_IN'):
86
+ return (Array.isArray(subject) || typeof subject === 'string') && !subject.includes(value);
87
+ case this.getState('conditional_step_comparators.EMPTY'):
88
+ return subject === '' || subject === null || subject === undefined || subject.length === 0;
89
+ case this.getState('conditional_step_comparators.NOT_EMPTY'):
90
+ return subject !== '' && subject !== null && subject !== undefined && subject.length > 0;
91
+ case this.getState('conditional_step_comparators.REGEX_MATCH'):
92
+ if (typeof value !== 'string') {
93
+ throw new Error(`Regex input must be a string.`);
94
+ }
95
+ const regex = new RegExp(value);
96
+ return regex.test(subject);
97
+ case this.getState('conditional_step_comparators.REGEX_NOT_MATCH'):
98
+ if (typeof value !== 'string') {
99
+ throw new Error(`Regex input must be a string.`);
100
+ }
101
+ const notMatchRegex = new RegExp(value);
102
+ return !notMatchRegex.test(subject);
103
+ case this.getState('conditional_step_comparators.STRING_STARTS_WITH'):
104
+ return typeof subject === 'string' && typeof value === 'string' && subject.startsWith(value);
105
+ case this.getState('conditional_step_comparators.STRING_ENDS_WITH'):
106
+ return typeof subject === 'string' && typeof value === 'string' && subject.endsWith(value);
107
+ case this.getState('conditional_step_comparators.NULLISH'):
108
+ return subject === null || subject === undefined;
109
+ case this.getState('conditional_step_comparators.NOT_NULLISH'):
110
+ return subject !== null && subject !== undefined;
111
+ case this.getState('conditional_step_comparators.IS_TYPE'):
112
+ return typeof subject === value;
113
+ case this.getState('conditional_step_comparators.IS_NOT_TYPE'):
114
+ return typeof subject !== value;
115
+ case this.getState('conditional_step_comparators.CUSTOM_FUNCTION'):
116
+ if (typeof value !== 'function') {
117
+ throw new Error(`Invalid custom function: ${value}`);
118
+ }
119
+ return value(subject);
120
+ default:
121
+ throw new Error(`Unknown operator: ${operator}`);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Checks if the conditional configuration is valid.
127
+ * @returns {boolean} True if conditional is valid.
128
+ */
129
+ conditionalIsValid() {
130
+ return (
131
+ this.conditional.subject !== null &&
132
+ this.conditional.subject !== undefined &&
133
+ this.conditional.operator !== null &&
134
+ this.conditional.operator !== undefined &&
135
+ this.conditional.value !== null &&
136
+ this.conditional.value !== undefined
137
+ );
138
+ }
139
+
140
+ /**
141
+ * Sets the conditional properties.
142
+ * @param {Object} conditional - Conditional configuration object.
143
+ * @throws {Error} Throws if conditional is invalid.
144
+ */
145
+ setConditional(conditional) {
146
+ this.conditional = { subject: conditional.subject, operator: conditional.operator, value: conditional.value };
147
+ }
148
+ }
@@ -0,0 +1,147 @@
1
+ import { loop_types, step_types } from '../../enums/index.js';
2
+ import LogicStep from './logic_step.js';
3
+ import { conditional_step_comparators } from '../../enums/index.js';
4
+
5
+ /**
6
+ * LoopStep class for executing loops within a workflow.
7
+ * @class LoopStep
8
+ * @extends LogicStep
9
+ */
10
+ export default class LoopStep extends LogicStep {
11
+ static step_name = step_types.LOOP;
12
+
13
+ /**
14
+ * Creates a new LoopStep instance.
15
+ * @param {Object} options - Configuration options.
16
+ * @param {string} [options.name] - Name of the step.
17
+ * @param {Array|Iterable|Function} options.iterable - Iterable to loop over or function returning an iterable. Required for 'for_each' and 'generator' loops.
18
+ * @param {Function} [options.callable=async () => {}] - Function to execute for each iteration.
19
+ * @param {Object} [options.conditional] - Conditional configuration for while loops. Required for 'while' loops.
20
+ * @param {*} [options.conditional.subject] - Subject to evaluate.
21
+ * @param {conditional_step_comparators|string} [options.conditional.operator] - Comparison operator.
22
+ * @param {*} [options.conditional.value] - Value to compare against.
23
+ * @param {string} [options.loop_type=loop_types.FOR_EACH] - Type of loop ('for', 'for_each', 'while', or 'generator').
24
+ * @param {number} [options.max_iterations=1000] - Maximum number of iterations to prevent infinite loops.
25
+ */
26
+ constructor({
27
+ name,
28
+ iterable,
29
+ callable = async () => {},
30
+ conditional = {
31
+ operator: null,
32
+ subject: null,
33
+ value: null,
34
+ },
35
+ loop_type = loop_types.FOR_EACH,
36
+ iterations = 0,
37
+ max_iterations = 1000,
38
+ }) {
39
+ super({ name, conditional });
40
+ this.iterable = iterable;
41
+ this.loop_type = loop_type;
42
+ this.iterations = iterations > max_iterations ? max_iterations : iterations;
43
+ this.max_iterations = max_iterations;
44
+ this.results = [];
45
+ this.current_item = null;
46
+
47
+ this.callable = this[`${loop_type}_loop`].bind(this);
48
+
49
+ // When this.callable is set, it binds the callable function to this instance
50
+ // so that it can access instance properties like current_item.
51
+ // Because we're using a local method instead, we bind it here.
52
+ this.callable_type = this.getCallableType(callable);
53
+ this._callable = this.callable_type === 'function'
54
+ ? callable.bind(this)
55
+ : callable.execute.bind(callable);
56
+ }
57
+
58
+ /**
59
+ * Executes a generator/async generator and appends yielded values to results.
60
+ * @throws {Error} If the callable is not a generator or async generator function.
61
+ * @returns {Object} - An object containing a message and the results of the loop.
62
+ */
63
+ async generator() {
64
+ if (!this._callable.constructor.name.includes('Generator')) {
65
+ throw new Error('Iterable must be a generator function for generator loops');
66
+ }
67
+
68
+ let iterations = 0;
69
+ for (const item of await this._callable()) {
70
+ this.results.push(item);
71
+
72
+ if (++iterations >= this.max_iterations) {
73
+ break;
74
+ }
75
+ }
76
+
77
+ return {
78
+ message: `Generator loop ${this.name} completed after ${iterations} iterations`,
79
+ result: this.results
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Executes a for loop calling the callable for a set number of iterations
85
+ * @returns {Object} - An object containing a message and the results of the loop.
86
+ */
87
+ async for_loop() {
88
+ let i = 0;
89
+ for (; i < this.iterations; i++) {
90
+ this.results.push(await this._callable());
91
+ }
92
+
93
+ return {
94
+ message: `For loop ${this.name} completed after ${i} iterations`,
95
+ result: this.results
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Executes the callable for each item in the iterable.
101
+ * @throws {Error} If the iterable is not provided.
102
+ * @returns {Object} - An object containing a message and the results of the loop.
103
+ */
104
+ async for_each_loop() {
105
+ if (!this.iterable) {
106
+ throw new Error('Iterable is required for for_each loops');
107
+ }
108
+
109
+ if (typeof this.iterable === 'function') {
110
+ this.iterable = this.iterable();
111
+ }
112
+
113
+ let iterations = 0;
114
+ for (const item of this.iterable) {
115
+ iterations++;
116
+ this.current_item = item;
117
+ this.results.push(await this._callable());
118
+ }
119
+
120
+ return {
121
+ message: `For each loop ${this.name} completed after ${iterations} iterations`,
122
+ result: this.results
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Executes the callable while the condition is true.
128
+ * @throws {Error} If the conditional is not valid.
129
+ * @returns {Object} - An object containing a message and the results of the loop.
130
+ */
131
+ async while_loop() {
132
+ if (!this.conditionalIsValid()) {
133
+ throw new Error('Valid conditional is required for while loops');
134
+ }
135
+
136
+ let iterations = 0;
137
+ while (this.checkCondition() && iterations < this.max_iterations) {
138
+ iterations++;
139
+ this.results.push(await this._callable());
140
+ }
141
+
142
+ return {
143
+ message: `While loop ${this.name} completed after ${iterations} iterations`,
144
+ result: this.results
145
+ };
146
+ }
147
+ }
@@ -0,0 +1,129 @@
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
+ #callable_object = null;
13
+
14
+ /**
15
+ * Creates a new Step instance.
16
+ * @param {Object} options - Configuration options.
17
+ * @param {string} [options.name] - Name of the step.
18
+ * @param {string} [options.step_type=step_types.ACTION] - Type of the step.
19
+ * @param {Function|Step|Workflow} [options.callable=async () => {}] - Function, Step, or Workflow to execute.
20
+ * @param {string} [options.sub_step_type=null] - Sub-type of the step.
21
+ */
22
+ constructor({
23
+ name,
24
+ step_type = step_types.ACTION,
25
+ callable = async () => {},
26
+ sub_step_type = null,
27
+ }) {
28
+ super({ name, base_type: base_types.STEP });
29
+
30
+ this.callable = callable;
31
+
32
+ // Store off the original callable object, because if it's a Step or Workflow,
33
+ // this.callable is set to the execute method of that object, but we may need to access its properties later.
34
+ this.#callable_object = callable;
35
+
36
+ this.step_type = step_type;
37
+ this.sub_step_type = sub_step_type;
38
+
39
+ this.errors = [];
40
+ this.result = null;
41
+ this.retry_results = [];
42
+ }
43
+
44
+ /**
45
+ * Executes the step's callable function, Step, or Workflow.
46
+ * @async
47
+ * @returns {Promise<Step>} The step instance with execution results.
48
+ */
49
+ async execute() {
50
+ this.markAsRunning();
51
+
52
+ try {
53
+ this.result = await this._callable();
54
+ } catch (error) {
55
+ this.errors.push(error);
56
+ this.markAsFailed();
57
+
58
+ this.timing.end_time = new Date();
59
+ this.timing.execution_time_ms = this.timing.end_time - this.timing.start_time;
60
+
61
+ if (this.getState('exit_on_error')) {
62
+ throw error;
63
+ }
64
+ }
65
+
66
+ if (this.status !== this.getState('statuses')[this.base_type].FAILED) {
67
+ this.markAsComplete();
68
+ }
69
+
70
+ if (['step', 'workflow'].includes(this.callable_type)) {
71
+ return this.#callable_object;
72
+ }
73
+
74
+ return this;
75
+ }
76
+
77
+ /**
78
+ * Determines the type of the callable (function, step, or workflow).
79
+ * @param {Function|Step|Workflow} callable - The callable to check.
80
+ * @returns {string} The type: 'function', 'step', or 'workflow'.
81
+ * @throws {Error} Throws if callable type is invalid.
82
+ */
83
+ getCallableType(callable) {
84
+ if (callable && callable.base_type === base_types.WORKFLOW) {
85
+ return 'workflow';
86
+ } else if (callable && callable.base_type === base_types.STEP) {
87
+ return 'step';
88
+ } else if (typeof callable === 'function') {
89
+ return 'function';
90
+ }
91
+
92
+ throw new Error('Invalid callable type. Must be one of function, Step, or Workflow.');
93
+ }
94
+
95
+ /**
96
+ * Sets a value in the parent workflow's state.
97
+ * @param {string} workflowId - ID of the parent workflow.
98
+ * @param {string} path - Path in the workflow state to set.
99
+ * @param {*} value - Value to set at the specified path.
100
+ * @throws {Error} Throws if parent workflow is not found.
101
+ */
102
+ setParentWorkflowValue(workflowId, path, value) {
103
+ const parentWorkflow = this.getState('workflows')[workflowId];
104
+
105
+ if (!parentWorkflow) {
106
+ throw new Error(`Parent workflow with ID ${workflowId} not found.`);
107
+ }
108
+
109
+ parentWorkflow[path] = value;
110
+ }
111
+
112
+ /**
113
+ * Sets the callable for the step and determines its type.
114
+ * @param {Function|Step|Workflow} callable - The callable to set.
115
+ */
116
+ set callable(callable) {
117
+ this.callable_type = this.getCallableType(callable);
118
+
119
+ if (['step', 'workflow'].includes(this.callable_type)) {
120
+ if (this.callable_type === 'step') {
121
+ callable.parentWorkflowId = this.parentWorkflowId ?? null;
122
+ }
123
+
124
+ this._callable = callable.execute.bind(callable);
125
+ } else {
126
+ this._callable = callable.bind(this);
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,64 @@
1
+ import Step from './step.js';
2
+
3
+ /**
4
+ * SwitchStep class for implementing switch/case logic in workflows.
5
+ * Evaluates cases in order and executes the first matching case, or a default callable if no cases match.
6
+ * @class SwitchStep
7
+ * @extends Step
8
+ */
9
+ export default class SwitchStep extends Step {
10
+ static step_name = 'switch';
11
+
12
+ /**
13
+ * Creates a new SwitchStep instance.
14
+ * @param {Object} options - Configuration options.
15
+ * @param {string} [options.name] - Name of the step.
16
+ * @param {Array<Case|LogicStep>} [options.cases=[]] - Array of Case or LogicStep instances to evaluate. LogicStep instances MUST have conditional.subject set.
17
+ * @param {Function|Step|Workflow} [options.default_callable=async () => {}] - Function, Step, or Workflow to execute if no cases match.
18
+ * @param {*} [options.subject=null] - Subject value to evaluate against each case.
19
+ */
20
+ constructor({
21
+ name,
22
+ cases = [],
23
+ default_callable = async () => {},
24
+ subject = null
25
+ }) {
26
+ super({
27
+ name,
28
+ step_type: SwitchStep.step_name,
29
+ });
30
+
31
+ this.cases = cases;
32
+ const defaultCallableType = this.getCallableType(default_callable);
33
+ this.default_callable = defaultCallableType === 'function'
34
+ ? default_callable.bind(this)
35
+ : default_callable.execute.bind(default_callable);
36
+ this.subject = subject;
37
+
38
+ this.callable = this.switch.bind(this);
39
+ }
40
+
41
+ /**
42
+ * Executes the switch logic by evaluating each case in order.
43
+ * Returns the result of the first matching case, or the default callable if no match.
44
+ * @returns {Promise<*>} The result of the matched case or default callable.
45
+ */
46
+ async switch() {
47
+ for (const switch_case of this.cases) {
48
+ switch_case.switch_subject = this.subject;
49
+
50
+ const is_matched = await switch_case.checkCondition();
51
+
52
+ if (is_matched) {
53
+ this.log(
54
+ this.getState('events.step.event_names.SWITCH_CASE_MATCHED'),
55
+ `Case matched for step: ${this.name}, executing case callable`
56
+ );
57
+
58
+ return switch_case.execute();
59
+ }
60
+ }
61
+
62
+ return this.default_callable();
63
+ }
64
+ }