@ronaldroe/micro-flow 1.3.9 → 2.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/README.md +51 -10
- package/dist/src/classes/base.js +2 -2
- package/dist/src/classes/base.js.map +3 -3
- package/dist/src/classes/callable_registry.js +1 -1
- package/dist/src/classes/callable_registry.js.map +2 -2
- package/dist/src/classes/events/event.js +1 -1
- package/dist/src/classes/events/event.js.map +3 -3
- package/dist/src/classes/index.js +1 -1
- package/dist/src/classes/index.js.map +3 -3
- package/dist/src/classes/state.js +1 -1
- package/dist/src/classes/state.js.map +3 -3
- package/dist/src/classes/steps/case.js +1 -1
- package/dist/src/classes/steps/case.js.map +3 -3
- package/dist/src/classes/steps/conditional_step.js +1 -1
- package/dist/src/classes/steps/conditional_step.js.map +3 -3
- package/dist/src/classes/steps/delay_step.js +1 -1
- package/dist/src/classes/steps/delay_step.js.map +2 -2
- package/dist/src/classes/steps/flow_control_step.js +1 -1
- package/dist/src/classes/steps/flow_control_step.js.map +2 -2
- package/dist/src/classes/steps/logic_step.js +1 -1
- package/dist/src/classes/steps/logic_step.js.map +3 -3
- package/dist/src/classes/steps/loop_step.js +1 -1
- package/dist/src/classes/steps/loop_step.js.map +3 -3
- package/dist/src/classes/steps/step.js +1 -1
- package/dist/src/classes/steps/step.js.map +3 -3
- package/dist/src/classes/steps/switch_step.js +1 -1
- package/dist/src/classes/steps/switch_step.js.map +3 -3
- package/dist/src/classes/workflow.js +1 -1
- package/dist/src/classes/workflow.js.map +3 -3
- package/dist/src/enums/delay_types.js.map +1 -1
- package/dist/src/enums/logic_step_types.js.map +3 -3
- package/dist/src/enums/sub_step_types.js +1 -1
- package/dist/src/enums/sub_step_types.js.map +2 -2
- package/package.json +1 -1
- package/src/classes/base.js +42 -11
- package/src/classes/callable_registry.js +82 -0
- package/src/classes/events/event.js +3 -3
- package/src/classes/index.js +2 -1
- package/src/classes/state.js +278 -8
- package/src/classes/steps/case.js +34 -4
- package/src/classes/steps/conditional_step.js +69 -3
- package/src/classes/steps/delay_step.js +18 -0
- package/src/classes/steps/flow_control_step.js +18 -2
- package/src/classes/steps/logic_step.js +26 -8
- package/src/classes/steps/loop_step.js +88 -3
- package/src/classes/steps/step.js +228 -16
- package/src/classes/steps/switch_step.js +66 -8
- package/src/classes/workflow.js +237 -40
- package/src/enums/delay_types.js +1 -1
- package/src/enums/logic_step_types.js +2 -2
- package/src/enums/sub_step_types.js +10 -10
package/src/classes/workflow.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
import Base from './base.js';
|
|
3
|
+
import CallableRegistry from './callable_registry.js';
|
|
4
|
+
import Step from './steps/step.js';
|
|
5
|
+
import State from './state.js';
|
|
3
6
|
import { base_types } from '../enums/index.js';
|
|
4
7
|
|
|
5
8
|
/**
|
|
@@ -12,30 +15,48 @@ export default class Workflow extends Base {
|
|
|
12
15
|
* Creates a new Workflow instance.
|
|
13
16
|
* @param {Object} options - Configuration options.
|
|
14
17
|
* @param {string} [options.name] - Name of the workflow.
|
|
18
|
+
* @param {CallableRegistry|null} [options.callable_registry=null] - Registry for callable objects.
|
|
15
19
|
* @param {boolean} [options.exit_on_error=false] - Whether to exit on error.
|
|
16
20
|
* @param {Array<Step>} [options.steps=[]] - Array of steps to add to the workflow.
|
|
17
21
|
* @param {boolean} [options.throw_on_empty=false] - Whether to throw error if workflow is empty.
|
|
22
|
+
* @param {boolean} [options.use_state_singleton=false] - Deprecated. When true, this workflow (and every
|
|
23
|
+
* `Step` it owns) reads/writes `getState`/`setState`/`deleteState` calls through the process-wide `State`
|
|
24
|
+
* singleton instead of this workflow's own state.
|
|
18
25
|
*/
|
|
19
26
|
constructor({
|
|
20
27
|
name,
|
|
28
|
+
callable_registry = null,
|
|
21
29
|
exit_on_error = false,
|
|
30
|
+
result_per_step = false,
|
|
31
|
+
result_per_step_function = null,
|
|
22
32
|
steps = [],
|
|
23
|
-
throw_on_empty = false
|
|
33
|
+
throw_on_empty = false,
|
|
34
|
+
use_state_singleton = false,
|
|
24
35
|
}) {
|
|
25
|
-
super({ name, base_type: base_types.WORKFLOW });
|
|
26
|
-
|
|
27
|
-
this.initializeWorkflowState();
|
|
28
|
-
|
|
29
|
-
this.addSteps(steps);
|
|
36
|
+
super({ name, base_type: base_types.WORKFLOW, use_state_singleton });
|
|
30
37
|
|
|
38
|
+
this.callable_registry = callable_registry ?? new CallableRegistry();
|
|
39
|
+
this.current_session_id = null;
|
|
31
40
|
this.exit_on_error = exit_on_error;
|
|
32
|
-
this.
|
|
41
|
+
this.result_per_step = result_per_step;
|
|
33
42
|
this.sessions = {};
|
|
34
|
-
this.
|
|
43
|
+
this.throw_on_empty = throw_on_empty;
|
|
44
|
+
this.result_per_step_function = result_per_step_function;
|
|
45
|
+
|
|
46
|
+
// _steps/steps_by_id must exist before initializeWorkflowState(): it reads this._steps
|
|
47
|
+
// (to set current_step) and logs, which serializes `this` - both need this._steps to
|
|
48
|
+
// already be an array, even when no steps are passed (addSteps([]) never calls addStep,
|
|
49
|
+
// so it wouldn't otherwise get initialized).
|
|
50
|
+
this._steps = [];
|
|
51
|
+
this.steps_by_id = {};
|
|
52
|
+
this.addSteps(steps);
|
|
53
|
+
this.initializeWorkflowState();
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
/**
|
|
38
57
|
* Executes the workflow by running all steps in sequence.
|
|
58
|
+
* If the workflow is currently `paused`, resumes from the step after the one
|
|
59
|
+
* that was running when it paused, rather than starting over from the beginning.
|
|
39
60
|
* @async
|
|
40
61
|
* @returns {Promise<Workflow>} The workflow instance with execution results.
|
|
41
62
|
* @throws {Error} Throws if workflow is empty and throw_on_empty is true.
|
|
@@ -51,13 +72,17 @@ export default class Workflow extends Base {
|
|
|
51
72
|
}
|
|
52
73
|
|
|
53
74
|
this.markAsComplete();
|
|
54
|
-
this.prepareResult('Workflow is empty', null);
|
|
75
|
+
await this.prepareResult('Workflow is empty', null);
|
|
55
76
|
return this;
|
|
56
77
|
}
|
|
57
|
-
|
|
78
|
+
|
|
79
|
+
const is_resuming = this.status === this.getState('statuses.workflow').PAUSED;
|
|
80
|
+
const paused_at_index = this._steps.findIndex(step => step.id === this.current_step);
|
|
81
|
+
const start_index = is_resuming ? paused_at_index + 1 : 0;
|
|
82
|
+
|
|
58
83
|
this.markAsRunning();
|
|
59
84
|
|
|
60
|
-
for (let i =
|
|
85
|
+
for (let i = start_index; i < this._steps.length; i++) {
|
|
61
86
|
if (this.should_break) {
|
|
62
87
|
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}.`);
|
|
63
88
|
break;
|
|
@@ -76,10 +101,10 @@ export default class Workflow extends Base {
|
|
|
76
101
|
|
|
77
102
|
try {
|
|
78
103
|
const step_result = await this.step();
|
|
79
|
-
this.prepareResult('Success', step_result);
|
|
104
|
+
await this.prepareResult('Success', step_result);
|
|
80
105
|
} catch (error) {
|
|
81
106
|
this.markAsFailed();
|
|
82
|
-
this.prepareResult(`Workflow execution failed at step ${this.steps_by_id[this.current_step].name} - ${this.current_step}`, { error });
|
|
107
|
+
await this.prepareResult(`Workflow execution failed at step ${this.steps_by_id[this.current_step].name} - ${this.current_step}`, { error });
|
|
83
108
|
|
|
84
109
|
if (this.exit_on_error) {
|
|
85
110
|
return this;
|
|
@@ -94,7 +119,7 @@ export default class Workflow extends Base {
|
|
|
94
119
|
}
|
|
95
120
|
|
|
96
121
|
this.markAsComplete();
|
|
97
|
-
return this;
|
|
122
|
+
return this.prepareForSerialization();
|
|
98
123
|
}
|
|
99
124
|
|
|
100
125
|
/**
|
|
@@ -121,7 +146,6 @@ export default class Workflow extends Base {
|
|
|
121
146
|
async step() {
|
|
122
147
|
const step = this.steps_by_id[this.current_step];
|
|
123
148
|
|
|
124
|
-
step.parentWorkflowId = this.id;
|
|
125
149
|
const result = await step.execute();
|
|
126
150
|
|
|
127
151
|
if (step.status === this.getState('statuses.step.FAILED')) {
|
|
@@ -137,8 +161,10 @@ export default class Workflow extends Base {
|
|
|
137
161
|
* @throws {Error} Throws if step is not a valid Step instance.
|
|
138
162
|
*/
|
|
139
163
|
addStep(step) {
|
|
164
|
+
// This check only ensures that the getCallableType method exists,
|
|
165
|
+
// which is a characteristic of Step instances
|
|
140
166
|
if (typeof step.getCallableType !== 'function') {
|
|
141
|
-
throw new Error('Invalid
|
|
167
|
+
throw new Error('Invalid input. Must be an instance of Step.');
|
|
142
168
|
}
|
|
143
169
|
|
|
144
170
|
if (!Array.isArray(this._steps)) {
|
|
@@ -151,7 +177,10 @@ export default class Workflow extends Base {
|
|
|
151
177
|
|
|
152
178
|
this.steps_by_id[step.id] = step;
|
|
153
179
|
|
|
154
|
-
step.
|
|
180
|
+
step.parent_workflow_id = this.id;
|
|
181
|
+
step.parent_workflow = this.prepareForSerialization();
|
|
182
|
+
step.use_state_singleton = this.use_state_singleton;
|
|
183
|
+
step.state = this.state;
|
|
155
184
|
this._steps.push(step);
|
|
156
185
|
}
|
|
157
186
|
|
|
@@ -166,7 +195,10 @@ export default class Workflow extends Base {
|
|
|
166
195
|
}
|
|
167
196
|
|
|
168
197
|
this.steps_by_id[step.id] = step;
|
|
169
|
-
step.
|
|
198
|
+
step.parent_workflow_id = this.id;
|
|
199
|
+
step.parent_workflow = this.prepareForSerialization();
|
|
200
|
+
step.use_state_singleton = this.use_state_singleton;
|
|
201
|
+
step.state = this.state;
|
|
170
202
|
this._steps.splice(index, 0, step);
|
|
171
203
|
}
|
|
172
204
|
|
|
@@ -175,6 +207,10 @@ export default class Workflow extends Base {
|
|
|
175
207
|
* @param {Step[]} steps - Array of steps to add.
|
|
176
208
|
*/
|
|
177
209
|
addSteps(steps) {
|
|
210
|
+
if (!Array.isArray(steps)) {
|
|
211
|
+
throw new Error('Invalid input. Must be an array of Step instances.');
|
|
212
|
+
}
|
|
213
|
+
|
|
178
214
|
steps.forEach(step => this.addStep(step));
|
|
179
215
|
}
|
|
180
216
|
|
|
@@ -183,6 +219,7 @@ export default class Workflow extends Base {
|
|
|
183
219
|
*/
|
|
184
220
|
clearSteps() {
|
|
185
221
|
this._steps = [];
|
|
222
|
+
this.steps_by_id = {};
|
|
186
223
|
}
|
|
187
224
|
|
|
188
225
|
/**
|
|
@@ -207,6 +244,10 @@ export default class Workflow extends Base {
|
|
|
207
244
|
* @param {string} stepId - The ID of the step to delete.
|
|
208
245
|
*/
|
|
209
246
|
deleteStep(stepId) {
|
|
247
|
+
if (!Array.isArray(this._steps)) {
|
|
248
|
+
this._steps = [];
|
|
249
|
+
}
|
|
250
|
+
|
|
210
251
|
this._steps = this._steps.filter(step => step.id !== stepId);
|
|
211
252
|
}
|
|
212
253
|
|
|
@@ -215,28 +256,48 @@ export default class Workflow extends Base {
|
|
|
215
256
|
* @param {number} index - The index of the step to delete.
|
|
216
257
|
*/
|
|
217
258
|
deleteStepByIndex(index) {
|
|
259
|
+
if (!Array.isArray(this._steps)) {
|
|
260
|
+
this._steps = [];
|
|
261
|
+
}
|
|
262
|
+
|
|
218
263
|
this._steps.splice(index, 1);
|
|
219
264
|
}
|
|
220
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Resolves a nested property path within this workflow's own state - the low-level counterpart
|
|
268
|
+
* to `getState()`. Falls back to the deprecated `State` singleton's resolver when
|
|
269
|
+
* `use_state_singleton` is `true`.
|
|
270
|
+
* @param {string} path - Path to the state property.
|
|
271
|
+
* @param {boolean} [emit=true] - Only meaningful when `use_state_singleton` is `true`; whether
|
|
272
|
+
* to emit the singleton's `GET_FROM_PROPERTY_PATH` state event.
|
|
273
|
+
* @returns {*} The value at the specified path, or undefined if not found.
|
|
274
|
+
*/
|
|
275
|
+
getStateFromPropertyPath(path, emit = true) {
|
|
276
|
+
if (this.use_state_singleton) {
|
|
277
|
+
console.warn('The state singleton has been deprecated. Use the .prepareForSerialization() method on the workflow instance instead.');
|
|
278
|
+
return State.getFromPropertyPath(path, emit);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return this.state.getStateFromPropertyPath(path);
|
|
282
|
+
}
|
|
283
|
+
|
|
221
284
|
/**
|
|
222
285
|
* Initializes the workflow state with default values.
|
|
223
286
|
*/
|
|
224
287
|
initializeWorkflowState() {
|
|
225
|
-
this.
|
|
226
|
-
this.
|
|
227
|
-
this.
|
|
228
|
-
this.should_break = false;
|
|
229
|
-
this.should_continue = false;
|
|
230
|
-
this.should_pause = false;
|
|
231
|
-
this.should_skip = false;
|
|
232
|
-
this.status = this.getState('statuses.workflow').CREATED;
|
|
233
|
-
this._steps = [];
|
|
234
|
-
this.throw_on_empty = this.throw_on_empty;
|
|
288
|
+
this.current_step = ! this.isEmpty() ? this._steps[0].id : null;
|
|
289
|
+
this.results = this.results ?? [];
|
|
290
|
+
this.sessions = this.sessions ?? {};
|
|
291
|
+
this.should_break = this.should_break ?? false;
|
|
292
|
+
this.should_continue = this.should_continue ?? false;
|
|
293
|
+
this.should_pause = this.should_pause ?? false;
|
|
294
|
+
this.should_skip = this.should_skip ?? false;
|
|
295
|
+
this.status = this.status ?? this.getState('statuses.workflow').CREATED;
|
|
235
296
|
this.timing = {
|
|
236
297
|
...this.timing,
|
|
237
|
-
create_time: new Date(),
|
|
238
|
-
pause_time: null,
|
|
239
|
-
resume_time: null,
|
|
298
|
+
create_time: this.timing?.create_time ?? new Date(),
|
|
299
|
+
pause_time: this.timing?.pause_time ?? null,
|
|
300
|
+
resume_time: this.timing?.resume_time ?? null,
|
|
240
301
|
}
|
|
241
302
|
|
|
242
303
|
const workflows = this.getState('workflows');
|
|
@@ -254,7 +315,7 @@ export default class Workflow extends Base {
|
|
|
254
315
|
* @returns {boolean} True if the workflow is empty.
|
|
255
316
|
*/
|
|
256
317
|
isEmpty() {
|
|
257
|
-
return !this._steps || !this._steps.length
|
|
318
|
+
return !Array.isArray(this._steps) || !this._steps.length;
|
|
258
319
|
}
|
|
259
320
|
|
|
260
321
|
/**
|
|
@@ -329,6 +390,16 @@ export default class Workflow extends Base {
|
|
|
329
390
|
);
|
|
330
391
|
}
|
|
331
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Parses a property path string into an array of keys, supporting both dot notation and
|
|
395
|
+
* bracket notation (e.g. `"users[0].name"`). Pure utility - not affected by `use_state_singleton`.
|
|
396
|
+
* @param {string} path - The path to parse.
|
|
397
|
+
* @returns {string[]} Array of property keys.
|
|
398
|
+
*/
|
|
399
|
+
parseStatePath(path) {
|
|
400
|
+
return this.use_state_singleton ? State.parsePath(path) : this.state.parseStatePath(path);
|
|
401
|
+
}
|
|
402
|
+
|
|
332
403
|
/**
|
|
333
404
|
* Pauses the workflow execution.
|
|
334
405
|
*/
|
|
@@ -350,13 +421,41 @@ export default class Workflow extends Base {
|
|
|
350
421
|
return this._steps.pop();
|
|
351
422
|
}
|
|
352
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Inserts safely serializable properties of the workflow into a new object for serialization.
|
|
426
|
+
* @returns {Object} An object containing the workflow's properties ready for serialization.
|
|
427
|
+
*/
|
|
428
|
+
prepareForSerialization() {
|
|
429
|
+
const serialized_workflow = {
|
|
430
|
+
id: this.id,
|
|
431
|
+
current_session_id: this.current_session_id,
|
|
432
|
+
current_step: this.current_step,
|
|
433
|
+
exit_on_error: this.exit_on_error,
|
|
434
|
+
name: this.name,
|
|
435
|
+
sessions: this.sessions,
|
|
436
|
+
status: this.status,
|
|
437
|
+
steps: this._steps.map(step => step.prepareForSerialization()),
|
|
438
|
+
throw_on_empty: this.throw_on_empty,
|
|
439
|
+
timing: this.timing,
|
|
440
|
+
results: this.results,
|
|
441
|
+
use_state_singleton: this.use_state_singleton,
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
return serialized_workflow;
|
|
445
|
+
}
|
|
446
|
+
|
|
353
447
|
/**
|
|
354
448
|
* Prepares a result object and adds it to the results array.
|
|
355
449
|
* @param {string} message - Result message.
|
|
356
450
|
* @param {*} data - Result data.
|
|
357
451
|
*/
|
|
358
|
-
prepareResult(message, data) {
|
|
359
|
-
this.
|
|
452
|
+
async prepareResult(message, data) {
|
|
453
|
+
if (this.result_per_step && typeof this.result_per_step_function === 'function') {
|
|
454
|
+
await this.result_per_step_function(this.prepareForSerialization());
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const result = { message, data };
|
|
458
|
+
this.results.push(result);
|
|
360
459
|
}
|
|
361
460
|
|
|
362
461
|
/**
|
|
@@ -375,6 +474,33 @@ export default class Workflow extends Base {
|
|
|
375
474
|
steps.forEach(step => this.addStep(step));
|
|
376
475
|
}
|
|
377
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Serializes the workflow into a JSON string.
|
|
479
|
+
* @returns {string} The JSON string representation of the workflow.
|
|
480
|
+
*/
|
|
481
|
+
serialize() {
|
|
482
|
+
return JSON.stringify(this.prepareForSerialization());
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Sets a nested property value within this workflow's own state, creating intermediate
|
|
487
|
+
* objects/arrays as needed - the low-level counterpart to `setState()`. Falls back to the
|
|
488
|
+
* deprecated `State` singleton's setter when `use_state_singleton` is `true`.
|
|
489
|
+
* @param {string} path - Path to the state property.
|
|
490
|
+
* @param {*} value - The value to set at the specified path.
|
|
491
|
+
* @param {boolean} [emit=true] - Only meaningful when `use_state_singleton` is `true`; whether
|
|
492
|
+
* to emit the singleton's `SET_TO_PROPERTY_PATH` state event.
|
|
493
|
+
*/
|
|
494
|
+
setStateToPropertyPath(path, value, emit = true) {
|
|
495
|
+
if (this.use_state_singleton) {
|
|
496
|
+
console.warn('The state singleton has been deprecated. Use the .prepareForSerialization() method on the workflow instance instead.');
|
|
497
|
+
State.setToPropertyPath(path, value, emit);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
this.state.setStateToPropertyPath(path, value);
|
|
502
|
+
}
|
|
503
|
+
|
|
378
504
|
/**
|
|
379
505
|
* Removes and returns the first step from the workflow.
|
|
380
506
|
* @returns {Step} The first step.
|
|
@@ -399,10 +525,20 @@ export default class Workflow extends Base {
|
|
|
399
525
|
|
|
400
526
|
this.steps_by_id[step.id] = step;
|
|
401
527
|
|
|
402
|
-
step.
|
|
528
|
+
step.parent_workflow_id = this.id;
|
|
529
|
+
step.use_state_singleton = this.use_state_singleton;
|
|
530
|
+
step.state = this.state;
|
|
403
531
|
this._steps.unshift(step);
|
|
404
532
|
}
|
|
405
533
|
|
|
534
|
+
/**
|
|
535
|
+
* Custom JSON serializer
|
|
536
|
+
* @returns {Object} The JSON representation of the workflow.
|
|
537
|
+
*/
|
|
538
|
+
toJSON() {
|
|
539
|
+
return this.prepareForSerialization();
|
|
540
|
+
}
|
|
541
|
+
|
|
406
542
|
/**
|
|
407
543
|
* Gets the array of steps in the workflow.
|
|
408
544
|
* @returns {Step[]} Array of steps.
|
|
@@ -416,12 +552,73 @@ export default class Workflow extends Base {
|
|
|
416
552
|
* @param {Step[]} steps - Array of steps to add.
|
|
417
553
|
*/
|
|
418
554
|
set steps(steps) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
555
|
+
this.addSteps(steps);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Deserializes a JSON string into a Workflow instance and hydrates it.
|
|
560
|
+
* @param {string} serialized_workflow - The JSON string representation of the workflow.
|
|
561
|
+
* @param {CallableRegistry|null} [callable_registry] - Registry used to resolve function callables in the workflow's steps.
|
|
562
|
+
* @returns {Workflow} The hydrated Workflow instance.
|
|
563
|
+
* @throws {Error} Throws if the serialized workflow is not a string.
|
|
564
|
+
*/
|
|
565
|
+
static hydrateSerialized(serialized_workflow, callable_registry = null) {
|
|
566
|
+
// TODO: Validate structure of serialized workflow
|
|
567
|
+
if (typeof serialized_workflow !== 'string') {
|
|
568
|
+
throw new Error('Invalid serialized workflow. Must be a string.');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const parsed = JSON.parse(serialized_workflow);
|
|
572
|
+
|
|
573
|
+
return Workflow.hydrate(parsed, callable_registry);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Hydrates a parsed workflow object into a Workflow instance.
|
|
578
|
+
* @param {Object} parsed_workflow - The parsed workflow object.
|
|
579
|
+
* @param {CallableRegistry|null} [callable_registry] - Registry used to resolve function callables in the workflow's steps.
|
|
580
|
+
* @returns {Workflow} The hydrated Workflow instance.
|
|
581
|
+
* @throws {Error} Throws if the parsed workflow is not a valid object.
|
|
582
|
+
*/
|
|
583
|
+
static hydrate(parsed_workflow, callable_registry = null) {
|
|
584
|
+
// TODO: Validate structure of serialized workflow
|
|
585
|
+
// TODO: Use event system to handle errors?
|
|
586
|
+
if (typeof parsed_workflow !== 'object' || parsed_workflow === null) {
|
|
587
|
+
throw new Error('Invalid parsed workflow. Must be a valid object.');
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const hydrated_workflow = new Workflow({
|
|
591
|
+
name: parsed_workflow.name,
|
|
592
|
+
callable_registry,
|
|
593
|
+
exit_on_error: parsed_workflow.exit_on_error,
|
|
594
|
+
steps: parsed_workflow.steps.map(step => Step.hydrateAny(step, callable_registry)),
|
|
595
|
+
throw_on_empty: parsed_workflow.throw_on_empty,
|
|
596
|
+
use_state_singleton: parsed_workflow.use_state_singleton ?? false,
|
|
423
597
|
});
|
|
424
598
|
|
|
425
|
-
|
|
599
|
+
// The constructor above (via Base) always generates a fresh id, and addStep() has
|
|
600
|
+
// already stamped that fresh id onto each step's parent_workflow_id and registered
|
|
601
|
+
// the workflow under it in its own state's `workflows` registry. Restoring the real id
|
|
602
|
+
// below would otherwise leave both of those referencing a discarded id, so fix them up here too.
|
|
603
|
+
const stale_id = hydrated_workflow.id;
|
|
604
|
+
hydrated_workflow.id = parsed_workflow.id;
|
|
605
|
+
|
|
606
|
+
const workflows = hydrated_workflow.getState('workflows');
|
|
607
|
+
delete workflows[stale_id];
|
|
608
|
+
workflows[hydrated_workflow.id] = hydrated_workflow;
|
|
609
|
+
hydrated_workflow.setState('workflows', workflows);
|
|
610
|
+
|
|
611
|
+
hydrated_workflow.steps.forEach(step => {
|
|
612
|
+
step.parent_workflow_id = hydrated_workflow.id;
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
hydrated_workflow.current_session_id = parsed_workflow.current_session_id;
|
|
616
|
+
hydrated_workflow.current_step = parsed_workflow.current_step ?? hydrated_workflow.current_step;
|
|
617
|
+
hydrated_workflow.sessions = parsed_workflow.sessions ?? {};
|
|
618
|
+
hydrated_workflow.status = parsed_workflow.status;
|
|
619
|
+
hydrated_workflow.timing = parsed_workflow.timing;
|
|
620
|
+
hydrated_workflow.results = parsed_workflow.results;
|
|
621
|
+
|
|
622
|
+
return hydrated_workflow;
|
|
426
623
|
}
|
|
427
624
|
}
|
package/src/enums/delay_types.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @enum {string}
|
|
6
6
|
* @readonly
|
|
7
7
|
*/
|
|
8
|
-
const
|
|
8
|
+
const logic_step_types = {
|
|
9
9
|
CONDITIONAL: 'conditional',
|
|
10
10
|
LOOP: 'loop',
|
|
11
11
|
FLOW_CONTROL: 'flow_control',
|
|
@@ -13,4 +13,4 @@ const LogicStepTypes = {
|
|
|
13
13
|
SKIP: 'skip'
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export default
|
|
16
|
+
export default logic_step_types;
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* @type {Object.<string, string>}
|
|
4
4
|
* @readonly
|
|
5
5
|
* @example
|
|
6
|
-
* console.log(sub_step_types.
|
|
7
|
-
* console.log(sub_step_types.
|
|
6
|
+
* console.log(sub_step_types.step); // "step"
|
|
7
|
+
* console.log(sub_step_types.conditional_step); // "conditional"
|
|
8
8
|
*/
|
|
9
9
|
const sub_step_types = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
step: 'step',
|
|
11
|
+
logic_step: 'logic',
|
|
12
|
+
conditional_step: 'conditional',
|
|
13
|
+
flow_control_step: 'flow_control',
|
|
14
|
+
loop_step: 'loop',
|
|
15
|
+
switch_step: 'switch',
|
|
16
|
+
case: 'case',
|
|
17
|
+
delay_step: 'delay',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export default sub_step_types;
|