@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.
- package/README.md +63 -153
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/src/classes/base.js +4 -0
- package/dist/src/classes/base.js.map +7 -0
- package/dist/src/classes/events/broadcast.js +2 -0
- package/dist/src/classes/events/broadcast.js.map +7 -0
- package/dist/src/classes/events/event.js +2 -0
- package/dist/src/classes/events/event.js.map +7 -0
- package/dist/src/classes/events/index.js +2 -0
- package/dist/src/classes/events/index.js.map +7 -0
- package/dist/src/classes/events/state_event.js +2 -0
- package/dist/src/classes/events/state_event.js.map +7 -0
- package/dist/src/classes/events/step_event.js +2 -0
- package/dist/src/classes/events/step_event.js.map +7 -0
- package/dist/src/classes/events/workflow_event.js +2 -0
- package/dist/src/classes/events/workflow_event.js.map +7 -0
- package/dist/src/classes/index.js +2 -0
- package/dist/src/classes/index.js.map +7 -0
- package/dist/src/classes/state.js +2 -0
- package/dist/src/classes/state.js.map +7 -0
- package/dist/src/classes/steps/case.js +2 -0
- package/dist/src/classes/steps/case.js.map +7 -0
- package/dist/src/classes/steps/conditional_step.js +2 -0
- package/dist/src/classes/steps/conditional_step.js.map +7 -0
- package/dist/src/classes/steps/delay_step.js +2 -0
- package/dist/src/classes/steps/delay_step.js.map +7 -0
- package/dist/src/classes/steps/flow_control_step.js +2 -0
- package/dist/src/classes/steps/flow_control_step.js.map +7 -0
- package/dist/src/classes/steps/index.js +2 -0
- package/dist/src/classes/steps/index.js.map +7 -0
- package/dist/src/classes/steps/logic_step.js +2 -0
- package/dist/src/classes/steps/logic_step.js.map +7 -0
- package/dist/src/classes/steps/loop_step.js +2 -0
- package/dist/src/classes/steps/loop_step.js.map +7 -0
- package/dist/src/classes/steps/step.js +2 -0
- package/dist/src/classes/steps/step.js.map +7 -0
- package/dist/src/classes/steps/switch_step.js +2 -0
- package/dist/src/classes/steps/switch_step.js.map +7 -0
- package/dist/src/classes/workflow.js +2 -0
- package/dist/src/classes/workflow.js.map +7 -0
- package/dist/src/classes/workflow.test.js +2 -0
- package/dist/src/classes/workflow.test.js.map +7 -0
- package/dist/src/enums/base_types.js +2 -0
- package/dist/src/enums/base_types.js.map +7 -0
- package/dist/src/enums/conditional_step_comparators.js +2 -0
- package/dist/src/enums/conditional_step_comparators.js.map +7 -0
- package/dist/src/enums/delay_types.js +2 -0
- package/dist/src/enums/delay_types.js.map +7 -0
- package/dist/src/enums/errors.js +6 -0
- package/dist/src/enums/errors.js.map +7 -0
- package/dist/src/enums/flow_control_types.js +2 -0
- package/dist/src/enums/flow_control_types.js.map +7 -0
- package/dist/src/enums/index.js +2 -0
- package/dist/src/enums/index.js.map +7 -0
- package/dist/src/enums/logic_step_types.js +2 -0
- package/dist/src/enums/logic_step_types.js.map +7 -0
- package/dist/src/enums/loop_types.js +2 -0
- package/dist/src/enums/loop_types.js.map +7 -0
- package/dist/src/enums/state_event_names.js +2 -0
- package/dist/src/enums/state_event_names.js.map +7 -0
- package/dist/src/enums/step_event_names.js +2 -0
- package/dist/src/enums/step_event_names.js.map +7 -0
- package/dist/src/enums/step_statuses.js +2 -0
- package/dist/src/enums/step_statuses.js.map +7 -0
- package/dist/src/enums/step_types.js +2 -0
- package/dist/src/enums/step_types.js.map +7 -0
- package/dist/src/enums/sub_step_types.js +2 -0
- package/dist/src/enums/sub_step_types.js.map +7 -0
- package/dist/src/enums/workflow_event_names.js +2 -0
- package/dist/src/enums/workflow_event_names.js.map +7 -0
- package/dist/src/enums/workflow_statuses.js +2 -0
- package/dist/src/enums/workflow_statuses.js.map +7 -0
- package/package.json +10 -6
- package/src/classes/base.js +4 -5
- package/src/classes/events/event.js +16 -9
- package/src/classes/events/index.js +1 -1
- package/src/classes/events/state_event.js +28 -0
- package/src/classes/state.js +161 -54
- package/src/classes/steps/case.js +70 -0
- package/src/classes/steps/conditional_step.js +20 -7
- package/src/classes/steps/delay_step.js +103 -0
- package/src/classes/steps/flow_control_step.js +8 -7
- package/src/classes/steps/index.js +4 -0
- package/src/classes/steps/logic_step.js +89 -29
- package/src/classes/steps/loop_step.js +148 -0
- package/src/classes/steps/step.js +59 -18
- package/src/classes/steps/switch_step.js +77 -0
- package/src/classes/workflow.js +74 -26
- package/src/enums/conditional_step_comparators.js +38 -8
- package/src/enums/delay_types.js +0 -7
- package/src/enums/errors.js +2 -0
- package/src/enums/index.js +1 -0
- package/src/enums/loop_types.js +3 -1
- package/src/enums/state_event_names.js +17 -0
- package/src/enums/step_event_names.js +3 -0
- package/src/enums/step_types.js +2 -1
- package/src/enums/sub_step_types.js +15 -125
- package/src/enums/workflow_event_names.js +1 -0
- package/index.js +0 -1
- 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
|
+
}
|
package/src/classes/workflow.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
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.
|
|
55
|
-
this.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|
package/src/enums/delay_types.js
CHANGED
|
@@ -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.
|
package/src/enums/errors.js
CHANGED
|
@@ -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
|
/**
|
package/src/enums/index.js
CHANGED
|
@@ -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';
|
package/src/enums/loop_types.js
CHANGED
|
@@ -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;
|
package/src/enums/step_types.js
CHANGED
|
@@ -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
|
-
*
|
|
105
|
-
*
|
|
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
|
-
*
|
|
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 =
|
|
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;
|
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
|
-
}
|