@ronaldroe/micro-flow 1.0.0 → 1.2.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.
Files changed (101) hide show
  1. package/README.md +63 -153
  2. package/dist/index.js +2 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/src/classes/base.js +4 -0
  5. package/dist/src/classes/base.js.map +7 -0
  6. package/dist/src/classes/events/broadcast.js +2 -0
  7. package/dist/src/classes/events/broadcast.js.map +7 -0
  8. package/dist/src/classes/events/event.js +2 -0
  9. package/dist/src/classes/events/event.js.map +7 -0
  10. package/dist/src/classes/events/index.js +2 -0
  11. package/dist/src/classes/events/index.js.map +7 -0
  12. package/dist/src/classes/events/state_event.js +2 -0
  13. package/dist/src/classes/events/state_event.js.map +7 -0
  14. package/dist/src/classes/events/step_event.js +2 -0
  15. package/dist/src/classes/events/step_event.js.map +7 -0
  16. package/dist/src/classes/events/workflow_event.js +2 -0
  17. package/dist/src/classes/events/workflow_event.js.map +7 -0
  18. package/dist/src/classes/index.js +2 -0
  19. package/dist/src/classes/index.js.map +7 -0
  20. package/dist/src/classes/state.js +2 -0
  21. package/dist/src/classes/state.js.map +7 -0
  22. package/dist/src/classes/steps/case.js +2 -0
  23. package/dist/src/classes/steps/case.js.map +7 -0
  24. package/dist/src/classes/steps/conditional_step.js +2 -0
  25. package/dist/src/classes/steps/conditional_step.js.map +7 -0
  26. package/dist/src/classes/steps/delay_step.js +2 -0
  27. package/dist/src/classes/steps/delay_step.js.map +7 -0
  28. package/dist/src/classes/steps/flow_control_step.js +2 -0
  29. package/dist/src/classes/steps/flow_control_step.js.map +7 -0
  30. package/dist/src/classes/steps/index.js +2 -0
  31. package/dist/src/classes/steps/index.js.map +7 -0
  32. package/dist/src/classes/steps/logic_step.js +2 -0
  33. package/dist/src/classes/steps/logic_step.js.map +7 -0
  34. package/dist/src/classes/steps/loop_step.js +2 -0
  35. package/dist/src/classes/steps/loop_step.js.map +7 -0
  36. package/dist/src/classes/steps/step.js +2 -0
  37. package/dist/src/classes/steps/step.js.map +7 -0
  38. package/dist/src/classes/steps/switch_step.js +2 -0
  39. package/dist/src/classes/steps/switch_step.js.map +7 -0
  40. package/dist/src/classes/workflow.js +2 -0
  41. package/dist/src/classes/workflow.js.map +7 -0
  42. package/dist/src/classes/workflow.test.js +2 -0
  43. package/dist/src/classes/workflow.test.js.map +7 -0
  44. package/dist/src/enums/base_types.js +2 -0
  45. package/dist/src/enums/base_types.js.map +7 -0
  46. package/dist/src/enums/conditional_step_comparators.js +2 -0
  47. package/dist/src/enums/conditional_step_comparators.js.map +7 -0
  48. package/dist/src/enums/delay_types.js +2 -0
  49. package/dist/src/enums/delay_types.js.map +7 -0
  50. package/dist/src/enums/errors.js +6 -0
  51. package/dist/src/enums/errors.js.map +7 -0
  52. package/dist/src/enums/flow_control_types.js +2 -0
  53. package/dist/src/enums/flow_control_types.js.map +7 -0
  54. package/dist/src/enums/index.js +2 -0
  55. package/dist/src/enums/index.js.map +7 -0
  56. package/dist/src/enums/logic_step_types.js +2 -0
  57. package/dist/src/enums/logic_step_types.js.map +7 -0
  58. package/dist/src/enums/loop_types.js +2 -0
  59. package/dist/src/enums/loop_types.js.map +7 -0
  60. package/dist/src/enums/state_event_names.js +2 -0
  61. package/dist/src/enums/state_event_names.js.map +7 -0
  62. package/dist/src/enums/step_event_names.js +2 -0
  63. package/dist/src/enums/step_event_names.js.map +7 -0
  64. package/dist/src/enums/step_statuses.js +2 -0
  65. package/dist/src/enums/step_statuses.js.map +7 -0
  66. package/dist/src/enums/step_types.js +2 -0
  67. package/dist/src/enums/step_types.js.map +7 -0
  68. package/dist/src/enums/sub_step_types.js +2 -0
  69. package/dist/src/enums/sub_step_types.js.map +7 -0
  70. package/dist/src/enums/workflow_event_names.js +2 -0
  71. package/dist/src/enums/workflow_event_names.js.map +7 -0
  72. package/dist/src/enums/workflow_statuses.js +2 -0
  73. package/dist/src/enums/workflow_statuses.js.map +7 -0
  74. package/package.json +10 -6
  75. package/src/classes/base.js +4 -5
  76. package/src/classes/events/event.js +16 -9
  77. package/src/classes/events/index.js +1 -1
  78. package/src/classes/events/state_event.js +28 -0
  79. package/src/classes/state.js +161 -54
  80. package/src/classes/steps/case.js +70 -0
  81. package/src/classes/steps/conditional_step.js +20 -7
  82. package/src/classes/steps/delay_step.js +103 -0
  83. package/src/classes/steps/flow_control_step.js +8 -7
  84. package/src/classes/steps/index.js +4 -0
  85. package/src/classes/steps/logic_step.js +89 -29
  86. package/src/classes/steps/loop_step.js +148 -0
  87. package/src/classes/steps/step.js +59 -18
  88. package/src/classes/steps/switch_step.js +77 -0
  89. package/src/classes/workflow.js +74 -26
  90. package/src/enums/conditional_step_comparators.js +38 -8
  91. package/src/enums/delay_types.js +0 -7
  92. package/src/enums/errors.js +2 -0
  93. package/src/enums/index.js +1 -0
  94. package/src/enums/loop_types.js +3 -1
  95. package/src/enums/state_event_names.js +17 -0
  96. package/src/enums/step_event_names.js +3 -0
  97. package/src/enums/step_types.js +2 -1
  98. package/src/enums/sub_step_types.js +15 -125
  99. package/src/enums/workflow_event_names.js +1 -0
  100. package/index.js +0 -1
  101. package/src/classes/events/broadcast.js +0 -57
@@ -0,0 +1,77 @@
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 {*|Function} [options.subject=null] - Subject value to evaluate against each case. Can be a function that returns the value.
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
+ this._default_callable_type = this.getCallableType(default_callable);
33
+ this._default_callable_raw = default_callable;
34
+ this.default_callable = this._default_callable_type === 'function'
35
+ ? default_callable.bind(this)
36
+ : default_callable.execute.bind(default_callable);
37
+ this.subject = subject;
38
+
39
+ this.callable = this.switch.bind(this);
40
+ }
41
+
42
+ /**
43
+ * Executes the switch logic by evaluating each case in order.
44
+ * Returns the result of the first matching case, or the default callable if no match.
45
+ * @returns {Promise<*>} The result of the matched case or default callable.
46
+ */
47
+ async switch() {
48
+ // Resolve subject once - call it if it's a function
49
+ const resolvedSubject = typeof this.subject === 'function' ? this.subject() : this.subject;
50
+
51
+ for (const switch_case of this.cases) {
52
+ switch_case.switch_subject = resolvedSubject;
53
+
54
+ const is_matched = await switch_case.checkCondition();
55
+
56
+ if (is_matched) {
57
+ this.log(
58
+ this.getState('events.step.event_names.SWITCH_CASE_MATCHED'),
59
+ `Case matched for step: ${this.name}, executing case callable`
60
+ );
61
+
62
+ // Return the case's result value directly, not the Case object.
63
+ // This keeps result structure consistent: switchStep.result contains the
64
+ // callable's return value, matching how Step.result works.
65
+ const caseResult = await switch_case.execute();
66
+ return caseResult.result;
67
+ }
68
+ }
69
+
70
+ // Unwrap Step/Workflow results for consistency with case results
71
+ const defaultResult = await this.default_callable();
72
+ if (this._default_callable_type !== 'function') {
73
+ return defaultResult.result;
74
+ }
75
+ return defaultResult;
76
+ }
77
+ }
@@ -1,4 +1,5 @@
1
- import { Base } from './index.js';
1
+ import crypto from 'crypto';
2
+ import Base from './base.js';
2
3
  import { base_types } from '../enums/index.js';
3
4
 
4
5
  /**
@@ -12,7 +13,7 @@ export default class Workflow extends Base {
12
13
  * @param {Object} options - Configuration options.
13
14
  * @param {string} [options.name] - Name of the workflow.
14
15
  * @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 {Array<Step>} [options.steps=[]] - Array of steps to add to the workflow.
16
17
  * @param {boolean} [options.throw_on_empty=false] - Whether to throw error if workflow is empty.
17
18
  */
18
19
  constructor({
@@ -29,6 +30,8 @@ export default class Workflow extends Base {
29
30
 
30
31
  this.exit_on_error = exit_on_error;
31
32
  this.throw_on_empty = throw_on_empty;
33
+ this.sessions = {};
34
+ this.current_session_id = null;
32
35
  }
33
36
 
34
37
  /**
@@ -38,6 +41,10 @@ export default class Workflow extends Base {
38
41
  * @throws {Error} Throws if workflow is empty and throw_on_empty is true.
39
42
  */
40
43
  async execute() {
44
+ if (!this.current_session_id) {
45
+ this.current_session_id = crypto.randomUUID();
46
+ }
47
+
41
48
  if (this.isEmpty()) {
42
49
  if (this.throw_on_empty) {
43
50
  throw new Error('Cannot execute an empty workflow');
@@ -51,23 +58,17 @@ export default class Workflow extends Base {
51
58
  this.markAsRunning();
52
59
 
53
60
  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}.`);
61
+ if (this.should_break) {
62
+ this.log(this.getState('event_names.workflow').WORKFLOW_BREAK_EXECUTED, `Workflow "${this.name}" execution broken at step ${this._steps[i].name} - ${this._steps[i].id}.`);
62
63
  break;
63
64
  }
64
65
 
65
- if (this.getState('should_skip')) {
66
+ if (this.should_skip) {
66
67
  this.log(
67
68
  this.getState('events.workflow.event_names.WORKFLOW_STEP_SKIPPED'),
68
69
  `Workflow "${this.name}" skipping step ${this._steps[i].name} - ${this._steps[i].id}.`
69
70
  );
70
- this.setState('should_skip', false);
71
+ this.should_skip = false;
71
72
  continue;
72
73
  }
73
74
 
@@ -84,6 +85,12 @@ export default class Workflow extends Base {
84
85
  return this;
85
86
  }
86
87
  }
88
+
89
+ if (this.should_pause) {
90
+ this.markAsPaused();
91
+ this.should_pause = false;
92
+ return this;
93
+ }
87
94
  }
88
95
 
89
96
  this.markAsComplete();
@@ -114,14 +121,11 @@ export default class Workflow extends Base {
114
121
  async step() {
115
122
  const step = this.steps_by_id[this.current_step];
116
123
 
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
- }
124
+ step.parentWorkflowId = this.id;
125
+ const result = await step.execute();
126
+
127
+ if (step.status === this.getState('statuses.step.FAILED')) {
128
+ throw step.errors[step.errors.length - 1] ?? new Error(`Step "${step.name}" failed`);
125
129
  }
126
130
 
127
131
  return result;
@@ -157,6 +161,11 @@ export default class Workflow extends Base {
157
161
  * @param {number} index - The index at which to insert the step.
158
162
  */
159
163
  addStepAtIndex(step, index) {
164
+ if (!this.steps_by_id || typeof this.steps_by_id !== 'object') {
165
+ this.steps_by_id = {};
166
+ }
167
+
168
+ this.steps_by_id[step.id] = step;
160
169
  step.parentWorkflowId = this.id;
161
170
  this._steps.splice(index, 0, step);
162
171
  }
@@ -176,6 +185,23 @@ export default class Workflow extends Base {
176
185
  this._steps = [];
177
186
  }
178
187
 
188
+ /**
189
+ * Closes the current session and stores a snapshot of the workflow state.
190
+ */
191
+ closeCurrentSession() {
192
+ if (!this.current_session_id) {
193
+ return;
194
+ }
195
+
196
+ this.sessions[this.current_session_id] = {
197
+ results: [...this.results],
198
+ status: this.status,
199
+ timing: { ...this.timing },
200
+ closed_at: new Date()
201
+ };
202
+ this.current_session_id = null;
203
+ }
204
+
179
205
  /**
180
206
  * Deletes a step from the workflow by its ID.
181
207
  * @param {string} stepId - The ID of the step to delete.
@@ -228,9 +254,17 @@ export default class Workflow extends Base {
228
254
  * @returns {boolean} True if the workflow is empty.
229
255
  */
230
256
  isEmpty() {
231
- return !this._steps && !this._steps.length
257
+ return !this._steps || !this._steps.length
232
258
  }
233
-
259
+
260
+ /**
261
+ * Marks the workflow as complete and closes the current session.
262
+ */
263
+ markAsComplete() {
264
+ super.markAsComplete();
265
+ this.closeCurrentSession();
266
+ }
267
+
234
268
  /**
235
269
  * Marks the workflow as created.
236
270
  * @returns {string} The CREATED status.
@@ -243,15 +277,23 @@ export default class Workflow extends Base {
243
277
  `Workflow "${this.name}" created.`
244
278
  );
245
279
 
246
- return this.getState('workflow.statuses').CREATED;
280
+ return this.getState('statuses.workflow').CREATED;
247
281
  }
248
-
282
+
283
+ /**
284
+ * Marks the workflow as failed and closes the current session.
285
+ */
286
+ markAsFailed() {
287
+ super.markAsFailed();
288
+ this.closeCurrentSession();
289
+ }
290
+
249
291
  /**
250
292
  * Marks the workflow as paused.
251
293
  */
252
294
  markAsPaused() {
253
295
  this.timing.pause_time = new Date();
254
- this.status = this.getState('workflow.statuses').PAUSED;
296
+ this.status = this.getState('statuses.workflow').PAUSED;
255
297
 
256
298
  this.getState('events.workflow').emit(
257
299
  this.getState('event_names.workflow').WORKFLOW_PAUSED,
@@ -264,7 +306,7 @@ export default class Workflow extends Base {
264
306
  */
265
307
  markAsResumed() {
266
308
  this.timing.resume_time = new Date();
267
- this.status = this.getState('workflow.statuses').RUNNING;
309
+ this.status = this.getState('statuses.workflow').RUNNING;
268
310
 
269
311
  this.getState('events.workflow').emit(
270
312
  this.getState('event_names.workflow').WORKFLOW_RESUMED,
@@ -374,6 +416,12 @@ export default class Workflow extends Base {
374
416
  * @param {Step[]} steps - Array of steps to add.
375
417
  */
376
418
  set steps(steps) {
419
+ steps.forEach((step, index) => {
420
+ if (typeof step.getCallableType !== 'function') {
421
+ throw new Error(`Invalid step type. Step at index ${index} is not an instance of Step.`);
422
+ }
423
+ });
424
+
377
425
  this.addSteps(steps);
378
426
  }
379
427
  }
@@ -7,21 +7,51 @@
7
7
  */
8
8
  const conditional_step_comparators = {
9
9
  EQUALS: 'equals',
10
- STRICT_EQUALS: 'strict_equals',
11
- NOT_EQUALS: 'not_equals',
12
- STRICT_NOT_EQUALS: 'strict_not_equals',
13
10
  GREATER_THAN: 'greater_than',
14
- LESS_THAN: 'less_than',
15
11
  GREATER_THAN_OR_EQUAL: 'greater_than_or_equal',
12
+ LESS_THAN: 'less_than',
16
13
  LESS_THAN_OR_EQUAL: 'less_than_or_equal',
14
+ NOT_EQUALS: 'not_equals',
15
+ STRICT_EQUALS: 'strict_equals',
16
+ STRICT_NOT_EQUALS: 'strict_not_equals',
17
+
17
18
  SIGN_EQUALS: '==',
18
- SIGN_STRICT_EQUALS: '===',
19
- SIGN_NOT_EQUALS: '!=',
20
- SIGN_STRICT_NOT_EQUALS: '!==',
21
19
  SIGN_GREATER_THAN: '>',
22
- SIGN_LESS_THAN: '<',
23
20
  SIGN_GREATER_THAN_OR_EQUAL: '>=',
21
+ SIGN_LESS_THAN: '<',
24
22
  SIGN_LESS_THAN_OR_EQUAL: '<=',
23
+ SIGN_NOT_EQUALS: '!=',
24
+ SIGN_STRICT_EQUALS: '===',
25
+ SIGN_STRICT_NOT_EQUALS: '!==',
26
+
27
+ STRING_CONTAINS: 'string_contains',
28
+ STRING_INCLUDES: 'string_includes',
29
+ STRING_NOT_CONTAINS: 'string_not_contains',
30
+ STRING_NOT_INCLUDES: 'string_not_includes',
31
+ STRING_STARTS_WITH: 'string_starts_with',
32
+ STRING_ENDS_WITH: 'string_ends_with',
33
+
34
+ ARRAY_CONTAINS: 'array_contains',
35
+ ARRAY_INCLUDES: 'array_includes',
36
+ ARRAY_NOT_CONTAINS: 'array_not_contains',
37
+ ARRAY_NOT_INCLUDES: 'array_not_includes',
38
+
39
+ EMPTY: 'empty',
40
+ NOT_EMPTY: 'not_empty',
41
+
42
+ REGEX_MATCH: 'regex_match',
43
+ REGEX_NOT_MATCH: 'regex_not_match',
44
+
45
+ IN: 'in',
46
+ NOT_IN: 'not_in',
47
+
48
+ NULLISH: 'nullish',
49
+ NOT_NULLISH: 'not_nullish',
50
+
51
+ IS_TYPE: 'is_type',
52
+ IS_NOT_TYPE: 'is_not_type',
53
+
54
+ CUSTOM_FUNCTION: 'custom_function'
25
55
  };
26
56
 
27
57
  export default conditional_step_comparators;
@@ -20,13 +20,6 @@ const delay_types = {
20
20
  */
21
21
  ABSOLUTE: 'absolute',
22
22
 
23
- /**
24
- * Delay using a cron expression for scheduled execution.
25
- * Use with cron_expression property.
26
- * @type {string}
27
- */
28
- CRON: 'cron',
29
-
30
23
  /**
31
24
  * Delay for a relative duration in milliseconds.
32
25
  * Use with delay_duration property.
@@ -7,6 +7,8 @@
7
7
  const errors = {
8
8
  INVALID_STATE_PATH: 'The provided state path is invalid.\n',
9
9
  INVALID_CONDITIONAL: 'Conditional properties are required for LogicStep.',
10
+ OBJECT_NOT_PARSEABLE: 'The provided object could not be parsed.\n',
11
+ VALUE_NOT_ITERABLE: 'The provided value is not iterable.\n',
10
12
  };
11
13
 
12
14
  /**
@@ -4,6 +4,7 @@ export * from './errors.js';
4
4
  export { default as flow_control_types } from './flow_control_types.js';
5
5
  export { default as logic_step_types } from './logic_step_types.js';
6
6
  export { default as loop_types } from './loop_types.js';
7
+ export { default as state_event_names } from './state_event_names.js';
7
8
  export { default as step_event_names } from './step_event_names.js';
8
9
  export { default as step_statuses } from './step_statuses.js';
9
10
  export { default as step_types } from './step_types.js';
@@ -5,8 +5,10 @@
5
5
  * @readonly
6
6
  */
7
7
  const loop_types = {
8
+ FOR: 'for',
9
+ FOR_EACH: 'for_each',
10
+ GENERATOR: 'generator',
8
11
  WHILE: 'while',
9
- FOR_EACH: 'for_each'
10
12
  };
11
13
 
12
14
  export default loop_types;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Enumeration of state event names.
3
+ */
4
+ const state_event_names = {
5
+ DELETED: 'deleted',
6
+ EACH: 'each',
7
+ FROZEN: 'frozen',
8
+ GET: 'get',
9
+ GET_FROM_PROPERTY_PATH: 'get_from_property_path',
10
+ GET_STATE: 'get_state',
11
+ MERGE: 'merge',
12
+ RESET: 'reset',
13
+ SET: 'set',
14
+ SET_TO_PROPERTY_PATH: 'set_to_property_path',
15
+ }
16
+
17
+ export default state_event_names;
@@ -8,6 +8,8 @@
8
8
  const step_event_names = {
9
9
  CONDITIONAL_FALSE_BRANCH_EXECUTED: 'conditional_false_branch_executed',
10
10
  CONDITIONAL_TRUE_BRANCH_EXECUTED: 'conditional_true_branch_executed',
11
+ DELAY_STEP_ABSOLUTE_SCHEDULED: 'delay_step_absolute_scheduled',
12
+ DELAY_STEP_RELATIVE_SCHEDULED: 'delay_step_relative_scheduled',
11
13
  DELAY_STEP_ABSOLUTE_COMPLETE: 'delay_step_absolute_complete',
12
14
  DELAY_STEP_RELATIVE_COMPLETE: 'delay_step_relative_complete',
13
15
  LOOP_ITERATION_COMPLETE: 'loop_iteration_complete',
@@ -17,6 +19,7 @@ const step_event_names = {
17
19
  STEP_PENDING: 'step_pending',
18
20
  STEP_WAITING: 'step_waiting',
19
21
  STEP_RETRYING: 'step_retrying',
22
+ SWITCH_CASE_MATCHED: 'switch_case_matched',
20
23
  }
21
24
 
22
25
  export default step_event_names;
@@ -7,8 +7,9 @@
7
7
  */
8
8
  const step_types = {
9
9
  ACTION: 'action',
10
- LOGIC: 'logic',
11
10
  DELAY: 'delay',
11
+ LOGIC: 'logic',
12
+ LOOP: 'loop',
12
13
  };
13
14
 
14
15
  export default step_types;
@@ -1,130 +1,20 @@
1
- import { readdirSync, readFileSync } from 'fs';
2
- import { fileURLToPath } from 'url';
3
- import { dirname, join } from 'path';
4
- import logic_step_types from './logic_step_types.js';
5
- import step_types from './step_types.js';
6
- import { sub } from 'date-fns';
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
10
-
11
- /**
12
- * Generates a mapping of Step class names to their step_name identifiers by reading
13
- * class files from the filesystem. This approach avoids circular dependencies that would
14
- * occur if we imported the Step classes directly.
15
- *
16
- * The function parses each class file to extract:
17
- * - The class name from the export statement
18
- * - The static step_name property value
19
- *
20
- * When step_name references an enum (e.g., logic_step_types.CONDITIONAL), the function
21
- * resolves the actual string value from the imported enum objects.
22
- *
23
- * The built-in classes directory is always scanned. Additional directories can be provided
24
- * to scan for custom step classes. This is typically used via the workflow's sub_step_type_paths
25
- * configuration option.
26
- *
27
- * @function generate_sub_step_types
28
- * @param {string[]} [directories=[]] - An array of additional directory paths to scan for class files.
29
- * The built-in classes directory is always included. Defaults to an empty array.
30
- * @returns {Object.<string, string|null>} An object mapping class names to their step_name values.
31
- * Classes without a step_name property are mapped to null.
32
- * @example
33
- * // Default usage (scans built-in classes only)
34
- * const types = generate_sub_step_types();
35
- * // Returns:
36
- * // {
37
- * // "Step": null,
38
- * // "ConditionalStep": "conditional",
39
- * // "LogicStep": "logic",
40
- * // "DelayStep": "delay",
41
- * // ...
42
- * // }
43
- *
44
- * // With custom directories
45
- * const types = generate_sub_step_types(['/path/to/custom/steps']);
46
- */
47
- const generate_sub_step_types = (directories = []) => {
48
- const types = {};
49
-
50
- // Always include the classes directory, then append any additional directories
51
- directories.push(join(__dirname, '../classes'));
52
-
53
- // Combine all enums into one lookup object
54
- const allEnums = {
55
- logic_step_types,
56
- step_types
57
- };
58
-
59
- // Scan each directory in the array
60
- for (const dir of directories) {
61
- // Read all files in the current directory
62
- const files = readdirSync(dir).filter(file => file.endsWith('.js') && file !== 'index.js');
63
-
64
- for (const file of files) {
65
- const filePath = join(dir, file);
66
- const content = readFileSync(filePath, 'utf-8');
67
-
68
- // Extract class name from the file content
69
- const classNameMatch = content.match(/export default class (\w+)/);
70
- if (!classNameMatch) continue;
71
-
72
- const className = classNameMatch[1];
73
-
74
- // Extract static step_name value
75
- const stepNameMatch = content.match(/static step_name\s*=\s*['"`]?([^'"`;\n]+)['"`]?/);
76
-
77
- if (stepNameMatch) {
78
- let stepName = stepNameMatch[1].trim();
79
-
80
- // Handle cases where step_name references an enum
81
- // e.g., logic_step_types.CONDITIONAL or step_types.ACTION
82
- if (stepName.includes('.')) {
83
- const [enumName, enumKey] = stepName.split('.');
84
-
85
- // Look up the actual value from the imported enums
86
- if (allEnums[enumName] && allEnums[enumName][enumKey]) {
87
- stepName = allEnums[enumName][enumKey];
88
- } else {
89
- stepName = null;
90
- }
91
- }
92
-
93
- types[className] = stepName;
94
- } else {
95
- types[className] = null;
96
- }
97
- }
98
- }
99
-
100
- return types;
101
- }
102
-
103
1
  /**
104
- * Pre-generated mapping of all built-in Step class names to their step_name identifiers.
105
- * This is a singleton object exported as the default export.
106
- *
107
- * **Changed in recent version:** Previously exported as a function, now exported as a
108
- * pre-generated object for improved performance and consistency.
109
- *
110
- * @constant {Object.<string, string|null>}
2
+ * Enumeration of sub-step types used in the workflow system.
3
+ * @type {Object.<string, string>}
4
+ * @readonly
111
5
  * @example
112
- * import sub_step_types from 'micro-flow';
113
- *
114
- * console.log(sub_step_types);
115
- * // {
116
- * // "Step": null,
117
- * // "ConditionalStep": "conditional",
118
- * // "DelayStep": "delay",
119
- * // "LoopStep": "loop",
120
- * // ...
121
- * // }
122
- *
123
- * // Check if a class name is a registered step type
124
- * if (sub_step_types['ConditionalStep']) {
125
- * console.log('ConditionalStep is a registered step type');
126
- * }
6
+ * console.log(sub_step_types.Step); // "step"
7
+ * console.log(sub_step_types.ConditionalStep); // "conditional"
127
8
  */
128
- const sub_step_types = generate_sub_step_types();
9
+ const sub_step_types = {
10
+ Step: 'step',
11
+ LogicStep: 'logic',
12
+ ConditionalStep: 'conditional',
13
+ FlowControlStep: 'flow_control',
14
+ LoopStep: 'loop',
15
+ SwitchStep: 'switch',
16
+ Case: 'case',
17
+ DelayStep: 'delay',
18
+ };
129
19
 
130
20
  export default sub_step_types;
@@ -6,6 +6,7 @@
6
6
  * @readonly
7
7
  */
8
8
  const workflow_event_names = {
9
+ WORKFLOW_BREAK_EXECUTED: 'workflow_break_executed',
9
10
  WORKFLOW_CANCELLED: 'workflow_cancelled',
10
11
  WORKFLOW_COMPLETE: 'workflow_complete',
11
12
  WORKFLOW_CREATED: 'workflow_created',
package/index.js DELETED
@@ -1 +0,0 @@
1
- export * from './src/index.js';
@@ -1,57 +0,0 @@
1
- /**
2
- * Broadcast class provides a simplified wrapper around the BroadcastChannel API
3
- * for cross-context communication (e.g., between tabs, windows, workers).
4
- *
5
- * This class allows you to send and receive messages across different browsing contexts
6
- * that share the same origin. It encapsulates the creation and management of a BroadcastChannel,
7
- * providing a simplified API for sending and receiving messages.
8
- *
9
- * @class Broadcast
10
- * @extends BroadcastChannel
11
- */
12
- export default class Broadcast extends BroadcastChannel {
13
- /**
14
- * Creates a new Broadcast instance for a named channel.
15
- *
16
- * @constructor
17
- * @param {string} channelName - The name of the broadcast channel to create or connect to.
18
- * Multiple Broadcast instances with the same channel name can communicate with each other.
19
- */
20
- constructor(channelName) {
21
- super(channelName);
22
- }
23
-
24
- /**
25
- * Sends data to all other contexts listening on this channel.
26
- *
27
- * @param {*} data - The data to broadcast. Can be any structured-cloneable value
28
- * (primitives, objects, arrays, etc.). Functions and DOM nodes cannot be sent.
29
- * @returns {void}
30
- */
31
- send(data) {
32
- this.postMessage(data);
33
- }
34
-
35
- /**
36
- * Registers a callback to handle incoming messages on this channel.
37
- *
38
- * @param {Function} callback - Function to call when a message is received.
39
- * Receives the message data as its only parameter.
40
- * @returns {void}
41
- */
42
- onReceive(callback) {
43
- this.onmessage = (event) => {
44
- callback(event.data);
45
- };
46
- }
47
-
48
- /**
49
- * Closes the broadcast channel and releases its resources.
50
- * After calling this method, the Broadcast instance can no longer send or receive messages.
51
- *
52
- * @returns {void}
53
- */
54
- destroy() {
55
- this.close();
56
- }
57
- }