@workglow/task-graph 0.0.85 → 0.0.87
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 +1 -53
- package/dist/browser.js +1451 -355
- package/dist/browser.js.map +28 -21
- package/dist/bun.js +1451 -355
- package/dist/bun.js.map +28 -21
- package/dist/common.d.ts +1 -16
- package/dist/common.d.ts.map +1 -1
- package/dist/node.js +1451 -355
- package/dist/node.js.map +28 -21
- package/dist/storage/TaskGraphTabularRepository.d.ts +2 -2
- package/dist/storage/TaskGraphTabularRepository.d.ts.map +1 -1
- package/dist/storage/TaskOutputTabularRepository.d.ts +2 -2
- package/dist/storage/TaskOutputTabularRepository.d.ts.map +1 -1
- package/dist/task/BatchTask.d.ts +154 -0
- package/dist/task/BatchTask.d.ts.map +1 -0
- package/dist/task/ConditionalTask.d.ts +1 -0
- package/dist/task/ConditionalTask.d.ts.map +1 -1
- package/dist/task/ForEachTask.d.ts +120 -0
- package/dist/task/ForEachTask.d.ts.map +1 -0
- package/dist/task/GraphAsTask.d.ts +2 -0
- package/dist/task/GraphAsTask.d.ts.map +1 -1
- package/dist/task/GraphAsTaskRunner.d.ts +0 -1
- package/dist/task/GraphAsTaskRunner.d.ts.map +1 -1
- package/dist/task/ITask.d.ts +5 -6
- package/dist/task/ITask.d.ts.map +1 -1
- package/dist/task/InputResolver.d.ts +34 -0
- package/dist/task/InputResolver.d.ts.map +1 -0
- package/dist/task/IteratorTask.d.ts +197 -0
- package/dist/task/IteratorTask.d.ts.map +1 -0
- package/dist/task/IteratorTaskRunner.d.ts +63 -0
- package/dist/task/IteratorTaskRunner.d.ts.map +1 -0
- package/dist/task/JobQueueTask.d.ts +3 -3
- package/dist/task/JobQueueTask.d.ts.map +1 -1
- package/dist/task/MapTask.d.ts +134 -0
- package/dist/task/MapTask.d.ts.map +1 -0
- package/dist/task/ReduceTask.d.ts +155 -0
- package/dist/task/ReduceTask.d.ts.map +1 -0
- package/dist/task/Task.d.ts +22 -11
- package/dist/task/Task.d.ts.map +1 -1
- package/dist/task/TaskEvents.d.ts +1 -1
- package/dist/task/TaskEvents.d.ts.map +1 -1
- package/dist/task/TaskJSON.d.ts +1 -4
- package/dist/task/TaskJSON.d.ts.map +1 -1
- package/dist/task/TaskRunner.d.ts +6 -5
- package/dist/task/TaskRunner.d.ts.map +1 -1
- package/dist/task/TaskTypes.d.ts +0 -4
- package/dist/task/TaskTypes.d.ts.map +1 -1
- package/dist/task/WhileTask.d.ts +176 -0
- package/dist/task/WhileTask.d.ts.map +1 -0
- package/dist/task/index.d.ts +35 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task-graph/Dataflow.d.ts +2 -3
- package/dist/task-graph/Dataflow.d.ts.map +1 -1
- package/dist/task-graph/ITaskGraph.d.ts +1 -1
- package/dist/task-graph/ITaskGraph.d.ts.map +1 -1
- package/dist/task-graph/TaskGraph.d.ts +4 -4
- package/dist/task-graph/TaskGraph.d.ts.map +1 -1
- package/dist/task-graph/TaskGraphRunner.d.ts +11 -18
- package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
- package/dist/task-graph/Workflow.d.ts +95 -9
- package/dist/task-graph/Workflow.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/storage/README.md +1 -1
- package/src/task/README.md +91 -15
- package/src/task-graph/README.md +0 -2
- package/dist/task/ArrayTask.d.ts +0 -77
- package/dist/task/ArrayTask.d.ts.map +0 -1
- package/dist/task/InputTask.d.ts +0 -27
- package/dist/task/InputTask.d.ts.map +0 -1
- package/dist/task/OutputTask.d.ts +0 -27
- package/dist/task/OutputTask.d.ts.map +0 -1
package/dist/browser.js
CHANGED
|
@@ -1,28 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
+
var __toCommonJS = (from) => {
|
|
7
|
+
var entry = __moduleCache.get(from), desc;
|
|
8
|
+
if (entry)
|
|
9
|
+
return entry;
|
|
10
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
+
get: () => from[key],
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
}));
|
|
16
|
+
__moduleCache.set(from, entry);
|
|
17
|
+
return entry;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
29
|
|
|
13
30
|
// src/task/TaskTypes.ts
|
|
14
|
-
var TaskStatus
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
var TaskStatus;
|
|
32
|
+
var init_TaskTypes = __esm(() => {
|
|
33
|
+
TaskStatus = {
|
|
34
|
+
PENDING: "PENDING",
|
|
35
|
+
DISABLED: "DISABLED",
|
|
36
|
+
PROCESSING: "PROCESSING",
|
|
37
|
+
COMPLETED: "COMPLETED",
|
|
38
|
+
ABORTING: "ABORTING",
|
|
39
|
+
FAILED: "FAILED"
|
|
40
|
+
};
|
|
41
|
+
});
|
|
22
42
|
|
|
23
43
|
// src/task-graph/Dataflow.ts
|
|
24
|
-
var
|
|
25
|
-
|
|
44
|
+
var exports_Dataflow = {};
|
|
45
|
+
__export(exports_Dataflow, {
|
|
46
|
+
DataflowArrow: () => DataflowArrow,
|
|
47
|
+
Dataflow: () => Dataflow,
|
|
48
|
+
DATAFLOW_ERROR_PORT: () => DATAFLOW_ERROR_PORT,
|
|
49
|
+
DATAFLOW_ALL_PORTS: () => DATAFLOW_ALL_PORTS
|
|
50
|
+
});
|
|
51
|
+
import { areSemanticallyCompatible, EventEmitter } from "@workglow/util";
|
|
26
52
|
|
|
27
53
|
class Dataflow {
|
|
28
54
|
sourceTaskId;
|
|
@@ -42,14 +68,12 @@ class Dataflow {
|
|
|
42
68
|
return Dataflow.createId(this.sourceTaskId, this.sourceTaskPortId, this.targetTaskId, this.targetTaskPortId);
|
|
43
69
|
}
|
|
44
70
|
value = undefined;
|
|
45
|
-
provenance = {};
|
|
46
71
|
status = TaskStatus.PENDING;
|
|
47
72
|
error;
|
|
48
73
|
reset() {
|
|
49
74
|
this.status = TaskStatus.PENDING;
|
|
50
75
|
this.error = undefined;
|
|
51
76
|
this.value = undefined;
|
|
52
|
-
this.provenance = {};
|
|
53
77
|
this.emit("reset");
|
|
54
78
|
this.emit("status", this.status);
|
|
55
79
|
}
|
|
@@ -79,7 +103,7 @@ class Dataflow {
|
|
|
79
103
|
}
|
|
80
104
|
this.emit("status", this.status);
|
|
81
105
|
}
|
|
82
|
-
setPortData(entireDataBlock
|
|
106
|
+
setPortData(entireDataBlock) {
|
|
83
107
|
if (this.sourceTaskPortId === DATAFLOW_ALL_PORTS) {
|
|
84
108
|
this.value = entireDataBlock;
|
|
85
109
|
} else if (this.sourceTaskPortId === DATAFLOW_ERROR_PORT) {
|
|
@@ -87,17 +111,17 @@ class Dataflow {
|
|
|
87
111
|
} else {
|
|
88
112
|
this.value = entireDataBlock[this.sourceTaskPortId];
|
|
89
113
|
}
|
|
90
|
-
if (nodeProvenance)
|
|
91
|
-
this.provenance = nodeProvenance;
|
|
92
114
|
}
|
|
93
115
|
getPortData() {
|
|
116
|
+
let result;
|
|
94
117
|
if (this.targetTaskPortId === DATAFLOW_ALL_PORTS) {
|
|
95
|
-
|
|
118
|
+
result = this.value;
|
|
96
119
|
} else if (this.targetTaskPortId === DATAFLOW_ERROR_PORT) {
|
|
97
|
-
|
|
120
|
+
result = { [DATAFLOW_ERROR_PORT]: this.error };
|
|
98
121
|
} else {
|
|
99
|
-
|
|
122
|
+
result = { [this.targetTaskPortId]: this.value };
|
|
100
123
|
}
|
|
124
|
+
return result;
|
|
101
125
|
}
|
|
102
126
|
toJSON() {
|
|
103
127
|
return {
|
|
@@ -159,23 +183,37 @@ class Dataflow {
|
|
|
159
183
|
this._events?.emit(name, ...args);
|
|
160
184
|
}
|
|
161
185
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
186
|
+
var DATAFLOW_ALL_PORTS = "*", DATAFLOW_ERROR_PORT = "[error]", DataflowArrow;
|
|
187
|
+
var init_Dataflow = __esm(() => {
|
|
188
|
+
init_TaskTypes();
|
|
189
|
+
DataflowArrow = class DataflowArrow extends Dataflow {
|
|
190
|
+
constructor(dataflow) {
|
|
191
|
+
const pattern = /^([a-zA-Z0-9-]+?)\[([a-zA-Z0-9-]+?)\] ==> ([a-zA-Z0-9-]+?)\[([a-zA-Z0-9-]+?)\]$/;
|
|
192
|
+
const match = dataflow.match(pattern);
|
|
193
|
+
if (!match) {
|
|
194
|
+
throw new Error(`Invalid dataflow format: ${dataflow}`);
|
|
195
|
+
}
|
|
196
|
+
const [, sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId] = match;
|
|
197
|
+
super(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
|
|
169
198
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// src/common.ts
|
|
203
|
+
init_Dataflow();
|
|
204
|
+
|
|
205
|
+
// src/task-graph/TaskGraph.ts
|
|
206
|
+
import { DirectedAcyclicGraph, EventEmitter as EventEmitter5, uuid4 as uuid44 } from "@workglow/util";
|
|
207
|
+
|
|
208
|
+
// src/task/GraphAsTask.ts
|
|
209
|
+
init_Dataflow();
|
|
210
|
+
import { compileSchema as compileSchema2 } from "@workglow/util";
|
|
174
211
|
|
|
175
212
|
// src/task-graph/TaskGraphRunner.ts
|
|
176
213
|
import {
|
|
177
214
|
collectPropertyValues,
|
|
178
215
|
globalServiceRegistry as globalServiceRegistry2,
|
|
216
|
+
ServiceRegistry as ServiceRegistry2,
|
|
179
217
|
uuid4 as uuid42
|
|
180
218
|
} from "@workglow/util";
|
|
181
219
|
|
|
@@ -210,6 +248,7 @@ class TaskOutputRepository {
|
|
|
210
248
|
}
|
|
211
249
|
|
|
212
250
|
// src/task/Task.ts
|
|
251
|
+
init_Dataflow();
|
|
213
252
|
import {
|
|
214
253
|
compileSchema,
|
|
215
254
|
deepEqual,
|
|
@@ -280,13 +319,71 @@ class TaskInvalidInputError extends TaskError {
|
|
|
280
319
|
|
|
281
320
|
// src/task/TaskRunner.ts
|
|
282
321
|
import { globalServiceRegistry } from "@workglow/util";
|
|
322
|
+
|
|
323
|
+
// src/task/InputResolver.ts
|
|
324
|
+
import { getInputResolvers } from "@workglow/util";
|
|
325
|
+
function getSchemaFormat(schema) {
|
|
326
|
+
if (typeof schema !== "object" || schema === null)
|
|
327
|
+
return;
|
|
328
|
+
const s = schema;
|
|
329
|
+
if (typeof s.format === "string")
|
|
330
|
+
return s.format;
|
|
331
|
+
const variants = s.oneOf ?? s.anyOf;
|
|
332
|
+
if (Array.isArray(variants)) {
|
|
333
|
+
for (const variant of variants) {
|
|
334
|
+
if (typeof variant === "object" && variant !== null) {
|
|
335
|
+
const v = variant;
|
|
336
|
+
if (typeof v.format === "string")
|
|
337
|
+
return v.format;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
function getFormatPrefix(format) {
|
|
344
|
+
const colonIndex = format.indexOf(":");
|
|
345
|
+
return colonIndex >= 0 ? format.substring(0, colonIndex) : format;
|
|
346
|
+
}
|
|
347
|
+
async function resolveSchemaInputs(input, schema, config) {
|
|
348
|
+
if (typeof schema === "boolean")
|
|
349
|
+
return input;
|
|
350
|
+
const properties = schema.properties;
|
|
351
|
+
if (!properties || typeof properties !== "object")
|
|
352
|
+
return input;
|
|
353
|
+
const resolvers = getInputResolvers();
|
|
354
|
+
const resolved = { ...input };
|
|
355
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
356
|
+
const value = resolved[key];
|
|
357
|
+
const format = getSchemaFormat(propSchema);
|
|
358
|
+
if (!format)
|
|
359
|
+
continue;
|
|
360
|
+
let resolver = resolvers.get(format);
|
|
361
|
+
if (!resolver) {
|
|
362
|
+
const prefix = getFormatPrefix(format);
|
|
363
|
+
resolver = resolvers.get(prefix);
|
|
364
|
+
}
|
|
365
|
+
if (!resolver)
|
|
366
|
+
continue;
|
|
367
|
+
if (typeof value === "string") {
|
|
368
|
+
resolved[key] = await resolver(value, format, config.registry);
|
|
369
|
+
} else if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
370
|
+
const results = await Promise.all(value.map((item) => resolver(item, format, config.registry)));
|
|
371
|
+
resolved[key] = results.filter((result) => result !== undefined);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return resolved;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/task/TaskRunner.ts
|
|
378
|
+
init_TaskTypes();
|
|
379
|
+
|
|
283
380
|
class TaskRunner {
|
|
284
381
|
running = false;
|
|
285
382
|
reactiveRunning = false;
|
|
286
|
-
nodeProvenance = {};
|
|
287
383
|
task;
|
|
288
384
|
abortController;
|
|
289
385
|
outputCache;
|
|
386
|
+
registry = globalServiceRegistry;
|
|
290
387
|
constructor(task) {
|
|
291
388
|
this.task = task;
|
|
292
389
|
this.own = this.own.bind(this);
|
|
@@ -296,6 +393,8 @@ class TaskRunner {
|
|
|
296
393
|
await this.handleStart(config);
|
|
297
394
|
try {
|
|
298
395
|
this.task.setInput(overrides);
|
|
396
|
+
const schema = this.task.constructor.inputSchema();
|
|
397
|
+
this.task.runInputData = await resolveSchemaInputs(this.task.runInputData, schema, { registry: this.registry });
|
|
299
398
|
const isValid = await this.task.validateInput(this.task.runInputData);
|
|
300
399
|
if (!isValid) {
|
|
301
400
|
throw new TaskInvalidInputError("Invalid input data");
|
|
@@ -331,6 +430,8 @@ class TaskRunner {
|
|
|
331
430
|
return this.task.runOutputData;
|
|
332
431
|
}
|
|
333
432
|
this.task.setInput(overrides);
|
|
433
|
+
const schema = this.task.constructor.inputSchema();
|
|
434
|
+
this.task.runInputData = await resolveSchemaInputs(this.task.runInputData, schema, { registry: this.registry });
|
|
334
435
|
await this.handleStartReactive();
|
|
335
436
|
try {
|
|
336
437
|
const isValid = await this.task.validateInput(this.task.runInputData);
|
|
@@ -361,8 +462,8 @@ class TaskRunner {
|
|
|
361
462
|
const result = await this.task.execute(input, {
|
|
362
463
|
signal: this.abortController.signal,
|
|
363
464
|
updateProgress: this.handleProgress.bind(this),
|
|
364
|
-
|
|
365
|
-
|
|
465
|
+
own: this.own,
|
|
466
|
+
registry: this.registry
|
|
366
467
|
});
|
|
367
468
|
return await this.executeTaskReactive(input, result || {});
|
|
368
469
|
}
|
|
@@ -373,7 +474,6 @@ class TaskRunner {
|
|
|
373
474
|
async handleStart(config = {}) {
|
|
374
475
|
if (this.task.status === TaskStatus.PROCESSING)
|
|
375
476
|
return;
|
|
376
|
-
this.nodeProvenance = {};
|
|
377
477
|
this.running = true;
|
|
378
478
|
this.task.startedAt = new Date;
|
|
379
479
|
this.task.progress = 0;
|
|
@@ -382,7 +482,6 @@ class TaskRunner {
|
|
|
382
482
|
this.abortController.signal.addEventListener("abort", () => {
|
|
383
483
|
this.handleAbort();
|
|
384
484
|
});
|
|
385
|
-
this.nodeProvenance = config.nodeProvenance ?? {};
|
|
386
485
|
const cache = this.task.config.outputCache ?? config.outputCache;
|
|
387
486
|
if (cache === true) {
|
|
388
487
|
let instance = globalServiceRegistry.get(TASK_OUTPUT_REPOSITORY);
|
|
@@ -395,6 +494,9 @@ class TaskRunner {
|
|
|
395
494
|
if (config.updateProgress) {
|
|
396
495
|
this.updateProgress = config.updateProgress;
|
|
397
496
|
}
|
|
497
|
+
if (config.registry) {
|
|
498
|
+
this.registry = config.registry;
|
|
499
|
+
}
|
|
398
500
|
this.task.emit("start");
|
|
399
501
|
this.task.emit("status", this.task.status);
|
|
400
502
|
}
|
|
@@ -421,7 +523,6 @@ class TaskRunner {
|
|
|
421
523
|
this.task.progress = 100;
|
|
422
524
|
this.task.status = TaskStatus.COMPLETED;
|
|
423
525
|
this.abortController = undefined;
|
|
424
|
-
this.nodeProvenance = {};
|
|
425
526
|
this.task.emit("complete");
|
|
426
527
|
this.task.emit("status", this.task.status);
|
|
427
528
|
}
|
|
@@ -435,7 +536,6 @@ class TaskRunner {
|
|
|
435
536
|
this.task.progress = 100;
|
|
436
537
|
this.task.completedAt = new Date;
|
|
437
538
|
this.abortController = undefined;
|
|
438
|
-
this.nodeProvenance = {};
|
|
439
539
|
this.task.emit("disabled");
|
|
440
540
|
this.task.emit("status", this.task.status);
|
|
441
541
|
}
|
|
@@ -455,7 +555,6 @@ class TaskRunner {
|
|
|
455
555
|
this.task.status = TaskStatus.FAILED;
|
|
456
556
|
this.task.error = err instanceof TaskError ? err : new TaskFailedError(err?.message || "Task failed");
|
|
457
557
|
this.abortController = undefined;
|
|
458
|
-
this.nodeProvenance = {};
|
|
459
558
|
this.task.emit("error", this.task.error);
|
|
460
559
|
this.task.emit("status", this.task.status);
|
|
461
560
|
}
|
|
@@ -470,6 +569,8 @@ class TaskRunner {
|
|
|
470
569
|
}
|
|
471
570
|
|
|
472
571
|
// src/task/Task.ts
|
|
572
|
+
init_TaskTypes();
|
|
573
|
+
|
|
473
574
|
class Task {
|
|
474
575
|
static type = "Task";
|
|
475
576
|
static category = "Hidden";
|
|
@@ -553,7 +654,6 @@ class Task {
|
|
|
553
654
|
return this._events;
|
|
554
655
|
}
|
|
555
656
|
_events;
|
|
556
|
-
nodeProvenance = {};
|
|
557
657
|
constructor(callerDefaultInputs = {}, config = {}) {
|
|
558
658
|
const inputDefaults = this.getDefaultInputsFromStaticInputDefinitions();
|
|
559
659
|
const mergedDefaults = Object.assign(inputDefaults, callerDefaultInputs);
|
|
@@ -590,10 +690,45 @@ class Task {
|
|
|
590
690
|
}
|
|
591
691
|
}
|
|
592
692
|
resetInputData() {
|
|
693
|
+
this.runInputData = this.smartClone(this.defaults);
|
|
694
|
+
}
|
|
695
|
+
smartClone(obj, visited = new WeakSet) {
|
|
696
|
+
if (obj === null || obj === undefined) {
|
|
697
|
+
return obj;
|
|
698
|
+
}
|
|
699
|
+
if (typeof obj !== "object") {
|
|
700
|
+
return obj;
|
|
701
|
+
}
|
|
702
|
+
if (visited.has(obj)) {
|
|
703
|
+
throw new Error("Circular reference detected in input data. " + "Cannot clone objects with circular references.");
|
|
704
|
+
}
|
|
705
|
+
if (ArrayBuffer.isView(obj)) {
|
|
706
|
+
if (typeof DataView !== "undefined" && obj instanceof DataView) {
|
|
707
|
+
return obj;
|
|
708
|
+
}
|
|
709
|
+
const typedArray = obj;
|
|
710
|
+
return new typedArray.constructor(typedArray);
|
|
711
|
+
}
|
|
712
|
+
if (!Array.isArray(obj)) {
|
|
713
|
+
const proto = Object.getPrototypeOf(obj);
|
|
714
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
715
|
+
return obj;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
visited.add(obj);
|
|
593
719
|
try {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
720
|
+
if (Array.isArray(obj)) {
|
|
721
|
+
return obj.map((item) => this.smartClone(item, visited));
|
|
722
|
+
}
|
|
723
|
+
const result = {};
|
|
724
|
+
for (const key in obj) {
|
|
725
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
726
|
+
result[key] = this.smartClone(obj[key], visited);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return result;
|
|
730
|
+
} finally {
|
|
731
|
+
visited.delete(obj);
|
|
597
732
|
}
|
|
598
733
|
}
|
|
599
734
|
setDefaults(defaults) {
|
|
@@ -621,7 +756,7 @@ class Task {
|
|
|
621
756
|
}
|
|
622
757
|
if (schema.additionalProperties === true) {
|
|
623
758
|
for (const [inputId, value] of Object.entries(input)) {
|
|
624
|
-
if (
|
|
759
|
+
if (!(inputId in properties)) {
|
|
625
760
|
this.runInputData[inputId] = value;
|
|
626
761
|
}
|
|
627
762
|
}
|
|
@@ -674,7 +809,7 @@ class Task {
|
|
|
674
809
|
}
|
|
675
810
|
if (inputSchema.additionalProperties === true) {
|
|
676
811
|
for (const [inputId, value] of Object.entries(overrides)) {
|
|
677
|
-
if (
|
|
812
|
+
if (!(inputId in properties)) {
|
|
678
813
|
if (!deepEqual(this.runInputData[inputId], value)) {
|
|
679
814
|
this.runInputData[inputId] = value;
|
|
680
815
|
changed = true;
|
|
@@ -684,7 +819,7 @@ class Task {
|
|
|
684
819
|
}
|
|
685
820
|
return changed;
|
|
686
821
|
}
|
|
687
|
-
async narrowInput(input) {
|
|
822
|
+
async narrowInput(input, _registry) {
|
|
688
823
|
return input;
|
|
689
824
|
}
|
|
690
825
|
subscribe(name, fn) {
|
|
@@ -744,20 +879,20 @@ class Task {
|
|
|
744
879
|
const path = e.data.pointer || "";
|
|
745
880
|
return `${e.message}${path ? ` (${path})` : ""}`;
|
|
746
881
|
});
|
|
747
|
-
throw new TaskInvalidInputError(`Input ${JSON.stringify(input)} does not match schema: ${errorMessages.join(", ")}`);
|
|
882
|
+
throw new TaskInvalidInputError(`Input ${JSON.stringify(Object.keys(input))} does not match schema: ${errorMessages.join(", ")}`);
|
|
748
883
|
}
|
|
749
884
|
return true;
|
|
750
885
|
}
|
|
751
886
|
id() {
|
|
752
887
|
return this.config.id;
|
|
753
888
|
}
|
|
754
|
-
getProvenance() {
|
|
755
|
-
return this.config.provenance ?? {};
|
|
756
|
-
}
|
|
757
889
|
stripSymbols(obj) {
|
|
758
890
|
if (obj === null || obj === undefined) {
|
|
759
891
|
return obj;
|
|
760
892
|
}
|
|
893
|
+
if (ArrayBuffer.isView(obj)) {
|
|
894
|
+
return obj;
|
|
895
|
+
}
|
|
761
896
|
if (Array.isArray(obj)) {
|
|
762
897
|
return obj.map((item) => this.stripSymbols(item));
|
|
763
898
|
}
|
|
@@ -773,14 +908,12 @@ class Task {
|
|
|
773
908
|
return obj;
|
|
774
909
|
}
|
|
775
910
|
toJSON() {
|
|
776
|
-
const provenance = this.getProvenance();
|
|
777
911
|
const extras = this.config.extras;
|
|
778
912
|
let json = this.stripSymbols({
|
|
779
913
|
id: this.config.id,
|
|
780
914
|
type: this.type,
|
|
781
915
|
...this.config.name ? { name: this.config.name } : {},
|
|
782
916
|
defaults: this.defaults,
|
|
783
|
-
...Object.keys(provenance).length ? { provenance } : {},
|
|
784
917
|
...extras && Object.keys(extras).length ? { extras } : {}
|
|
785
918
|
});
|
|
786
919
|
return json;
|
|
@@ -826,8 +959,9 @@ class Task {
|
|
|
826
959
|
// src/task/ConditionalTask.ts
|
|
827
960
|
class ConditionalTask extends Task {
|
|
828
961
|
static type = "ConditionalTask";
|
|
829
|
-
static category = "
|
|
962
|
+
static category = "Hidden";
|
|
830
963
|
static title = "Conditional Task";
|
|
964
|
+
static description = "Evaluates conditions to determine which dataflows are active";
|
|
831
965
|
static hasDynamicSchemas = true;
|
|
832
966
|
activeBranches = new Set;
|
|
833
967
|
async execute(input, context) {
|
|
@@ -935,7 +1069,13 @@ class ConditionalTask extends Task {
|
|
|
935
1069
|
}
|
|
936
1070
|
}
|
|
937
1071
|
|
|
1072
|
+
// src/task-graph/TaskGraphRunner.ts
|
|
1073
|
+
init_TaskTypes();
|
|
1074
|
+
init_Dataflow();
|
|
1075
|
+
|
|
938
1076
|
// src/task-graph/TaskGraphScheduler.ts
|
|
1077
|
+
init_TaskTypes();
|
|
1078
|
+
|
|
939
1079
|
class TopologicalScheduler {
|
|
940
1080
|
dag;
|
|
941
1081
|
sortedNodes;
|
|
@@ -1052,9 +1192,9 @@ class TaskGraphRunner {
|
|
|
1052
1192
|
reactiveScheduler;
|
|
1053
1193
|
running = false;
|
|
1054
1194
|
reactiveRunning = false;
|
|
1055
|
-
provenanceInput;
|
|
1056
1195
|
graph;
|
|
1057
1196
|
outputCache;
|
|
1197
|
+
registry = globalServiceRegistry2;
|
|
1058
1198
|
abortController;
|
|
1059
1199
|
inProgressTasks = new Map;
|
|
1060
1200
|
inProgressFunctions = new Map;
|
|
@@ -1063,7 +1203,6 @@ class TaskGraphRunner {
|
|
|
1063
1203
|
this.processScheduler = processScheduler;
|
|
1064
1204
|
this.reactiveScheduler = reactiveScheduler;
|
|
1065
1205
|
this.graph = graph;
|
|
1066
|
-
this.provenanceInput = new Map;
|
|
1067
1206
|
graph.outputCache = outputCache;
|
|
1068
1207
|
this.handleProgress = this.handleProgress.bind(this);
|
|
1069
1208
|
}
|
|
@@ -1083,7 +1222,7 @@ class TaskGraphRunner {
|
|
|
1083
1222
|
const runAsync = async () => {
|
|
1084
1223
|
try {
|
|
1085
1224
|
const taskInput = isRootTask ? input : this.filterInputForTask(task, input);
|
|
1086
|
-
const taskPromise = this.
|
|
1225
|
+
const taskPromise = this.runTask(task, taskInput);
|
|
1087
1226
|
this.inProgressTasks.set(task.config.id, taskPromise);
|
|
1088
1227
|
const taskResult = await taskPromise;
|
|
1089
1228
|
if (this.graph.getTargetDataflows(task.config.id).length === 0) {
|
|
@@ -1193,23 +1332,16 @@ class TaskGraphRunner {
|
|
|
1193
1332
|
this.addInputData(task, dataflow.getPortData());
|
|
1194
1333
|
}
|
|
1195
1334
|
}
|
|
1196
|
-
|
|
1197
|
-
const nodeProvenance = {};
|
|
1198
|
-
this.graph.getSourceDataflows(node.config.id).forEach((dataflow) => {
|
|
1199
|
-
Object.assign(nodeProvenance, dataflow.provenance);
|
|
1200
|
-
});
|
|
1201
|
-
return nodeProvenance;
|
|
1202
|
-
}
|
|
1203
|
-
async pushOutputFromNodeToEdges(node, results, nodeProvenance) {
|
|
1335
|
+
async pushOutputFromNodeToEdges(node, results) {
|
|
1204
1336
|
const dataflows = this.graph.getTargetDataflows(node.config.id);
|
|
1205
1337
|
for (const dataflow of dataflows) {
|
|
1206
1338
|
const compatibility = dataflow.semanticallyCompatible(this.graph, dataflow);
|
|
1207
1339
|
if (compatibility === "static") {
|
|
1208
|
-
dataflow.setPortData(results
|
|
1340
|
+
dataflow.setPortData(results);
|
|
1209
1341
|
} else if (compatibility === "runtime") {
|
|
1210
1342
|
const task = this.graph.getTask(dataflow.targetTaskId);
|
|
1211
|
-
const narrowed = await task.narrowInput({ ...results });
|
|
1212
|
-
dataflow.setPortData(narrowed
|
|
1343
|
+
const narrowed = await task.narrowInput({ ...results }, this.registry);
|
|
1344
|
+
dataflow.setPortData(narrowed);
|
|
1213
1345
|
} else {}
|
|
1214
1346
|
}
|
|
1215
1347
|
}
|
|
@@ -1279,20 +1411,14 @@ class TaskGraphRunner {
|
|
|
1279
1411
|
}
|
|
1280
1412
|
}
|
|
1281
1413
|
}
|
|
1282
|
-
async
|
|
1283
|
-
const nodeProvenance = {
|
|
1284
|
-
...parentProvenance,
|
|
1285
|
-
...this.getInputProvenance(task),
|
|
1286
|
-
...task.getProvenance()
|
|
1287
|
-
};
|
|
1288
|
-
this.provenanceInput.set(task.config.id, nodeProvenance);
|
|
1414
|
+
async runTask(task, input) {
|
|
1289
1415
|
this.copyInputFromEdgesToNode(task);
|
|
1290
1416
|
const results = await task.runner.run(input, {
|
|
1291
|
-
nodeProvenance,
|
|
1292
1417
|
outputCache: this.outputCache,
|
|
1293
|
-
updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args)
|
|
1418
|
+
updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
|
|
1419
|
+
registry: this.registry
|
|
1294
1420
|
});
|
|
1295
|
-
await this.pushOutputFromNodeToEdges(task, results
|
|
1421
|
+
await this.pushOutputFromNodeToEdges(task, results);
|
|
1296
1422
|
return {
|
|
1297
1423
|
id: task.config.id,
|
|
1298
1424
|
type: task.constructor.runtype || task.constructor.type,
|
|
@@ -1326,10 +1452,15 @@ class TaskGraphRunner {
|
|
|
1326
1452
|
});
|
|
1327
1453
|
}
|
|
1328
1454
|
async handleStart(config) {
|
|
1455
|
+
if (config?.registry !== undefined) {
|
|
1456
|
+
this.registry = config.registry;
|
|
1457
|
+
} else {
|
|
1458
|
+
this.registry = new ServiceRegistry2(globalServiceRegistry2.container.createChildContainer());
|
|
1459
|
+
}
|
|
1329
1460
|
if (config?.outputCache !== undefined) {
|
|
1330
1461
|
if (typeof config.outputCache === "boolean") {
|
|
1331
1462
|
if (config.outputCache === true) {
|
|
1332
|
-
this.outputCache =
|
|
1463
|
+
this.outputCache = this.registry.get(TASK_OUTPUT_REPOSITORY);
|
|
1333
1464
|
} else {
|
|
1334
1465
|
this.outputCache = undefined;
|
|
1335
1466
|
}
|
|
@@ -1415,7 +1546,7 @@ class TaskGraphRunner {
|
|
|
1415
1546
|
progress = Math.round(completed / total);
|
|
1416
1547
|
}
|
|
1417
1548
|
this.pushStatusFromNodeToEdges(this.graph, task);
|
|
1418
|
-
await this.pushOutputFromNodeToEdges(task, task.runOutputData
|
|
1549
|
+
await this.pushOutputFromNodeToEdges(task, task.runOutputData);
|
|
1419
1550
|
this.graph.emit("graph_progress", progress, message, args);
|
|
1420
1551
|
}
|
|
1421
1552
|
}
|
|
@@ -1427,7 +1558,6 @@ class GraphAsTaskRunner extends TaskRunner {
|
|
|
1427
1558
|
this.task.emit("progress", progress, message, ...args);
|
|
1428
1559
|
});
|
|
1429
1560
|
const results = await this.task.subGraph.run(input, {
|
|
1430
|
-
parentProvenance: this.nodeProvenance || {},
|
|
1431
1561
|
parentSignal: this.abortController?.signal,
|
|
1432
1562
|
outputCache: this.outputCache
|
|
1433
1563
|
});
|
|
@@ -1443,27 +1573,12 @@ class GraphAsTaskRunner extends TaskRunner {
|
|
|
1443
1573
|
}
|
|
1444
1574
|
super.handleDisable();
|
|
1445
1575
|
}
|
|
1446
|
-
fixInput(input) {
|
|
1447
|
-
const inputSchema = this.task.inputSchema();
|
|
1448
|
-
if (typeof inputSchema === "boolean") {
|
|
1449
|
-
return input;
|
|
1450
|
-
}
|
|
1451
|
-
const flattenedInput = Object.entries(input).reduce((acc, [key, value]) => {
|
|
1452
|
-
const inputDef = inputSchema.properties?.[key];
|
|
1453
|
-
const shouldFlatten = Array.isArray(value) && typeof inputDef === "object" && inputDef !== null && "x-replicate" in inputDef && inputDef["x-replicate"] === true;
|
|
1454
|
-
if (shouldFlatten) {
|
|
1455
|
-
return { ...acc, [key]: value[0] };
|
|
1456
|
-
}
|
|
1457
|
-
return { ...acc, [key]: value };
|
|
1458
|
-
}, {});
|
|
1459
|
-
return flattenedInput;
|
|
1460
|
-
}
|
|
1461
1576
|
async executeTask(input) {
|
|
1462
1577
|
if (this.task.hasChildren()) {
|
|
1463
1578
|
const runExecuteOutputData = await this.executeTaskChildren(input);
|
|
1464
1579
|
this.task.runOutputData = this.task.subGraph.mergeExecuteOutputsToRunOutput(runExecuteOutputData, this.task.compoundMerge);
|
|
1465
1580
|
} else {
|
|
1466
|
-
const result = await super.executeTask(
|
|
1581
|
+
const result = await super.executeTask(input);
|
|
1467
1582
|
this.task.runOutputData = result ?? {};
|
|
1468
1583
|
}
|
|
1469
1584
|
return this.task.runOutputData;
|
|
@@ -1473,7 +1588,7 @@ class GraphAsTaskRunner extends TaskRunner {
|
|
|
1473
1588
|
const reactiveResults = await this.executeTaskChildrenReactive();
|
|
1474
1589
|
this.task.runOutputData = this.task.subGraph.mergeExecuteOutputsToRunOutput(reactiveResults, this.task.compoundMerge);
|
|
1475
1590
|
} else {
|
|
1476
|
-
const reactiveResults = await super.executeTaskReactive(
|
|
1591
|
+
const reactiveResults = await super.executeTaskReactive(input, output);
|
|
1477
1592
|
this.task.runOutputData = Object.assign({}, output, reactiveResults ?? {});
|
|
1478
1593
|
}
|
|
1479
1594
|
return this.task.runOutputData;
|
|
@@ -1483,6 +1598,8 @@ class GraphAsTaskRunner extends TaskRunner {
|
|
|
1483
1598
|
// src/task/GraphAsTask.ts
|
|
1484
1599
|
class GraphAsTask extends Task {
|
|
1485
1600
|
static type = "GraphAsTask";
|
|
1601
|
+
static title = "Graph as Task";
|
|
1602
|
+
static description = "A task that contains a subgraph of tasks";
|
|
1486
1603
|
static category = "Hidden";
|
|
1487
1604
|
static compoundMerge = PROPERTY_ARRAY;
|
|
1488
1605
|
static hasDynamicSchemas = true;
|
|
@@ -1662,35 +1779,70 @@ class GraphAsTask extends Task {
|
|
|
1662
1779
|
}
|
|
1663
1780
|
}
|
|
1664
1781
|
|
|
1782
|
+
// src/task-graph/Conversions.ts
|
|
1783
|
+
init_Dataflow();
|
|
1784
|
+
|
|
1665
1785
|
// src/task-graph/Workflow.ts
|
|
1666
|
-
import { EventEmitter as EventEmitter4 } from "@workglow/util";
|
|
1786
|
+
import { EventEmitter as EventEmitter4, uuid4 as uuid43 } from "@workglow/util";
|
|
1787
|
+
init_Dataflow();
|
|
1788
|
+
function CreateWorkflow(taskClass) {
|
|
1789
|
+
return Workflow.createWorkflow(taskClass);
|
|
1790
|
+
}
|
|
1791
|
+
function CreateLoopWorkflow(taskClass) {
|
|
1792
|
+
return function(config = {}) {
|
|
1793
|
+
const task = new taskClass({}, config);
|
|
1794
|
+
this.graph.addTask(task);
|
|
1795
|
+
const previousTask = getLastTask(this);
|
|
1796
|
+
if (previousTask && previousTask !== task) {
|
|
1797
|
+
this.graph.addDataflow(new Dataflow(previousTask.config.id, "*", task.config.id, "*"));
|
|
1798
|
+
}
|
|
1799
|
+
return new Workflow(this.outputCache(), this, task);
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function CreateEndLoopWorkflow(methodName) {
|
|
1803
|
+
return function() {
|
|
1804
|
+
if (!this.isLoopBuilder) {
|
|
1805
|
+
throw new Error(`${methodName}() can only be called on loop workflows`);
|
|
1806
|
+
}
|
|
1807
|
+
return this.finalizeAndReturn();
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1667
1811
|
class WorkflowTask extends GraphAsTask {
|
|
1668
1812
|
static type = "Workflow";
|
|
1669
1813
|
static compoundMerge = PROPERTY_ARRAY;
|
|
1670
1814
|
}
|
|
1671
|
-
var taskIdCounter = 0;
|
|
1672
1815
|
|
|
1673
1816
|
class Workflow {
|
|
1674
|
-
constructor(
|
|
1675
|
-
this.
|
|
1676
|
-
this.
|
|
1677
|
-
|
|
1678
|
-
});
|
|
1679
|
-
|
|
1680
|
-
|
|
1817
|
+
constructor(cache, parent, iteratorTask) {
|
|
1818
|
+
this._outputCache = cache;
|
|
1819
|
+
this._parentWorkflow = parent;
|
|
1820
|
+
this._iteratorTask = iteratorTask;
|
|
1821
|
+
this._graph = new TaskGraph({ outputCache: this._outputCache });
|
|
1822
|
+
if (!parent) {
|
|
1823
|
+
this._onChanged = this._onChanged.bind(this);
|
|
1824
|
+
this.setupEvents();
|
|
1825
|
+
}
|
|
1681
1826
|
}
|
|
1682
1827
|
_graph;
|
|
1683
1828
|
_dataFlows = [];
|
|
1684
1829
|
_error = "";
|
|
1685
|
-
|
|
1830
|
+
_outputCache;
|
|
1686
1831
|
_abortController;
|
|
1832
|
+
_parentWorkflow;
|
|
1833
|
+
_iteratorTask;
|
|
1834
|
+
outputCache() {
|
|
1835
|
+
return this._outputCache;
|
|
1836
|
+
}
|
|
1837
|
+
get isLoopBuilder() {
|
|
1838
|
+
return this._parentWorkflow !== undefined;
|
|
1839
|
+
}
|
|
1687
1840
|
events = new EventEmitter4;
|
|
1688
1841
|
static createWorkflow(taskClass) {
|
|
1689
1842
|
const helper = function(input = {}, config = {}) {
|
|
1690
1843
|
this._error = "";
|
|
1691
1844
|
const parent = getLastTask(this);
|
|
1692
|
-
|
|
1693
|
-
const task = this.addTask(taskClass, input, { id: String(taskIdCounter), ...config });
|
|
1845
|
+
const task = this.addTaskToGraph(taskClass, input, { id: uuid43(), ...config });
|
|
1694
1846
|
if (this._dataFlows.length > 0) {
|
|
1695
1847
|
this._dataFlows.forEach((dataflow) => {
|
|
1696
1848
|
const taskSchema = task.inputSchema();
|
|
@@ -1705,42 +1857,26 @@ class Workflow {
|
|
|
1705
1857
|
this._dataFlows = [];
|
|
1706
1858
|
}
|
|
1707
1859
|
if (parent && this.graph.getTargetDataflows(parent.config.id).length === 0) {
|
|
1708
|
-
const
|
|
1709
|
-
const
|
|
1710
|
-
const
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
matches.set(toInputPortId, fromOutputPortId);
|
|
1719
|
-
this.connect(parent.config.id, fromOutputPortId, task.config.id, toInputPortId);
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
return matches;
|
|
1724
|
-
};
|
|
1725
|
-
makeMatch(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
|
|
1726
|
-
if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
|
|
1727
|
-
if (fromPortOutputSchema === true && toPortInputSchema === true) {
|
|
1728
|
-
return true;
|
|
1729
|
-
}
|
|
1730
|
-
return false;
|
|
1731
|
-
}
|
|
1732
|
-
const idTypeMatch = fromPortOutputSchema.$id !== undefined && fromPortOutputSchema.$id === toPortInputSchema.$id;
|
|
1733
|
-
const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
|
|
1734
|
-
const typeMatch = idTypeBlank && (fromPortOutputSchema.type === toPortInputSchema.type || (toPortInputSchema.oneOf?.some((i) => i.type == fromPortOutputSchema.type) ?? false));
|
|
1735
|
-
const outputPortIdMatch = fromOutputPortId === toInputPortId;
|
|
1736
|
-
const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
|
|
1737
|
-
const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
|
|
1738
|
-
return (idTypeMatch || typeMatch) && portIdsCompatible;
|
|
1860
|
+
const nodes = this._graph.getTasks();
|
|
1861
|
+
const parentIndex = nodes.findIndex((n) => n.config.id === parent.config.id);
|
|
1862
|
+
const earlierTasks = [];
|
|
1863
|
+
for (let i = parentIndex - 1;i >= 0; i--) {
|
|
1864
|
+
earlierTasks.push(nodes[i]);
|
|
1865
|
+
}
|
|
1866
|
+
const providedInputKeys = new Set(Object.keys(input || {}));
|
|
1867
|
+
const result = Workflow.autoConnect(this.graph, parent, task, {
|
|
1868
|
+
providedInputKeys,
|
|
1869
|
+
earlierTasks
|
|
1739
1870
|
});
|
|
1740
|
-
if (
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1871
|
+
if (result.error) {
|
|
1872
|
+
if (this.isLoopBuilder) {
|
|
1873
|
+
this._error = result.error;
|
|
1874
|
+
console.warn(this._error);
|
|
1875
|
+
} else {
|
|
1876
|
+
this._error = result.error + " Task not added.";
|
|
1877
|
+
console.error(this._error);
|
|
1878
|
+
this.graph.removeTask(task.config.id);
|
|
1879
|
+
}
|
|
1744
1880
|
}
|
|
1745
1881
|
}
|
|
1746
1882
|
return this;
|
|
@@ -1780,13 +1916,16 @@ class Workflow {
|
|
|
1780
1916
|
return this.events.waitOn(name);
|
|
1781
1917
|
}
|
|
1782
1918
|
async run(input = {}) {
|
|
1919
|
+
if (this.isLoopBuilder) {
|
|
1920
|
+
this.finalizeTemplate();
|
|
1921
|
+
return this._parentWorkflow.run(input);
|
|
1922
|
+
}
|
|
1783
1923
|
this.events.emit("start");
|
|
1784
1924
|
this._abortController = new AbortController;
|
|
1785
1925
|
try {
|
|
1786
1926
|
const output = await this.graph.run(input, {
|
|
1787
1927
|
parentSignal: this._abortController.signal,
|
|
1788
|
-
|
|
1789
|
-
outputCache: this._repository
|
|
1928
|
+
outputCache: this._outputCache
|
|
1790
1929
|
});
|
|
1791
1930
|
const results = this.graph.mergeExecuteOutputsToRunOutput(output, PROPERTY_ARRAY);
|
|
1792
1931
|
this.events.emit("complete");
|
|
@@ -1799,6 +1938,9 @@ class Workflow {
|
|
|
1799
1938
|
}
|
|
1800
1939
|
}
|
|
1801
1940
|
async abort() {
|
|
1941
|
+
if (this._parentWorkflow) {
|
|
1942
|
+
return this._parentWorkflow.abort();
|
|
1943
|
+
}
|
|
1802
1944
|
this._abortController?.abort();
|
|
1803
1945
|
}
|
|
1804
1946
|
pop() {
|
|
@@ -1867,10 +2009,12 @@ class Workflow {
|
|
|
1867
2009
|
return task;
|
|
1868
2010
|
}
|
|
1869
2011
|
reset() {
|
|
1870
|
-
|
|
2012
|
+
if (this._parentWorkflow) {
|
|
2013
|
+
throw new WorkflowError("Cannot reset a loop workflow. Call reset() on the parent workflow.");
|
|
2014
|
+
}
|
|
1871
2015
|
this.clearEvents();
|
|
1872
2016
|
this._graph = new TaskGraph({
|
|
1873
|
-
outputCache: this.
|
|
2017
|
+
outputCache: this._outputCache
|
|
1874
2018
|
});
|
|
1875
2019
|
this._dataFlows = [];
|
|
1876
2020
|
this._error = "";
|
|
@@ -1917,22 +2061,202 @@ class Workflow {
|
|
|
1917
2061
|
if (targetSchema === false) {
|
|
1918
2062
|
throw new WorkflowError(`Target task has schema 'false' and accepts no inputs`);
|
|
1919
2063
|
}
|
|
1920
|
-
|
|
2064
|
+
if (targetSchema === true) {}
|
|
2065
|
+
} else if (targetSchema.additionalProperties === true) {} else if (!targetSchema.properties?.[targetTaskPortId]) {
|
|
1921
2066
|
throw new WorkflowError(`Input ${targetTaskPortId} not found on target task`);
|
|
1922
2067
|
}
|
|
1923
2068
|
const dataflow = new Dataflow(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
|
|
1924
2069
|
this.graph.addDataflow(dataflow);
|
|
1925
2070
|
return this;
|
|
1926
2071
|
}
|
|
1927
|
-
|
|
2072
|
+
addTaskToGraph(taskClass, input, config) {
|
|
1928
2073
|
const task = new taskClass(input, config);
|
|
1929
2074
|
const id = this.graph.addTask(task);
|
|
1930
2075
|
this.events.emit("changed", id);
|
|
1931
2076
|
return task;
|
|
1932
2077
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2078
|
+
addTask(taskClass, input, config) {
|
|
2079
|
+
const helper = Workflow.createWorkflow(taskClass);
|
|
2080
|
+
return helper.call(this, input, config);
|
|
2081
|
+
}
|
|
2082
|
+
static AutoConnectOptions = Symbol("AutoConnectOptions");
|
|
2083
|
+
static autoConnect(graph, sourceTask, targetTask, options) {
|
|
2084
|
+
const matches = new Map;
|
|
2085
|
+
const sourceSchema = sourceTask.outputSchema();
|
|
2086
|
+
const targetSchema = targetTask.inputSchema();
|
|
2087
|
+
const providedInputKeys = options?.providedInputKeys ?? new Set;
|
|
2088
|
+
const earlierTasks = options?.earlierTasks ?? [];
|
|
2089
|
+
const getSpecificTypeIdentifiers = (schema) => {
|
|
2090
|
+
const formats = new Set;
|
|
2091
|
+
const ids = new Set;
|
|
2092
|
+
if (typeof schema === "boolean") {
|
|
2093
|
+
return { formats, ids };
|
|
2094
|
+
}
|
|
2095
|
+
const extractFromSchema = (s) => {
|
|
2096
|
+
if (!s || typeof s !== "object" || Array.isArray(s))
|
|
2097
|
+
return;
|
|
2098
|
+
if (s.format)
|
|
2099
|
+
formats.add(s.format);
|
|
2100
|
+
if (s.$id)
|
|
2101
|
+
ids.add(s.$id);
|
|
2102
|
+
};
|
|
2103
|
+
extractFromSchema(schema);
|
|
2104
|
+
const checkUnion = (schemas) => {
|
|
2105
|
+
if (!schemas)
|
|
2106
|
+
return;
|
|
2107
|
+
for (const s of schemas) {
|
|
2108
|
+
if (typeof s === "boolean")
|
|
2109
|
+
continue;
|
|
2110
|
+
extractFromSchema(s);
|
|
2111
|
+
if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
|
|
2112
|
+
extractFromSchema(s.items);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
2116
|
+
checkUnion(schema.oneOf);
|
|
2117
|
+
checkUnion(schema.anyOf);
|
|
2118
|
+
if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
|
|
2119
|
+
extractFromSchema(schema.items);
|
|
2120
|
+
}
|
|
2121
|
+
return { formats, ids };
|
|
2122
|
+
};
|
|
2123
|
+
const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
|
|
2124
|
+
if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
|
|
2125
|
+
return fromPortOutputSchema === true && toPortInputSchema === true;
|
|
2126
|
+
}
|
|
2127
|
+
const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
|
|
2128
|
+
const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
|
|
2129
|
+
for (const format of outputIds.formats) {
|
|
2130
|
+
if (inputIds.formats.has(format)) {
|
|
2131
|
+
return true;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
for (const id of outputIds.ids) {
|
|
2135
|
+
if (inputIds.ids.has(id)) {
|
|
2136
|
+
return true;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
if (requireSpecificType) {
|
|
2140
|
+
return false;
|
|
2141
|
+
}
|
|
2142
|
+
const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
|
|
2143
|
+
if (!idTypeBlank)
|
|
2144
|
+
return false;
|
|
2145
|
+
if (fromPortOutputSchema.type === toPortInputSchema.type)
|
|
2146
|
+
return true;
|
|
2147
|
+
const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
|
|
2148
|
+
if (typeof schema === "boolean")
|
|
2149
|
+
return schema;
|
|
2150
|
+
return schema.type === fromPortOutputSchema.type;
|
|
2151
|
+
}) ?? false;
|
|
2152
|
+
const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
|
|
2153
|
+
if (typeof schema === "boolean")
|
|
2154
|
+
return schema;
|
|
2155
|
+
return schema.type === fromPortOutputSchema.type;
|
|
2156
|
+
}) ?? false;
|
|
2157
|
+
return matchesOneOf || matchesAnyOf;
|
|
2158
|
+
};
|
|
2159
|
+
const makeMatch = (fromSchema, toSchema, fromTaskId, toTaskId, comparator) => {
|
|
2160
|
+
if (typeof fromSchema === "object") {
|
|
2161
|
+
if (toSchema === true || typeof toSchema === "object" && toSchema.additionalProperties === true) {
|
|
2162
|
+
for (const fromOutputPortId of Object.keys(fromSchema.properties || {})) {
|
|
2163
|
+
matches.set(fromOutputPortId, fromOutputPortId);
|
|
2164
|
+
graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
|
|
2165
|
+
}
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
if (typeof fromSchema === "boolean" || typeof toSchema === "boolean") {
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(fromSchema.properties || {})) {
|
|
2173
|
+
for (const [toInputPortId, toPortInputSchema] of Object.entries(toSchema.properties || {})) {
|
|
2174
|
+
if (!matches.has(toInputPortId) && comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
|
|
2175
|
+
matches.set(toInputPortId, fromOutputPortId);
|
|
2176
|
+
graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, toInputPortId));
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
};
|
|
2181
|
+
makeMatch(sourceSchema, targetSchema, sourceTask.config.id, targetTask.config.id, ([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
|
|
2182
|
+
const outputPortIdMatch = fromOutputPortId === toInputPortId;
|
|
2183
|
+
const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
|
|
2184
|
+
const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
|
|
2185
|
+
return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
|
|
2186
|
+
});
|
|
2187
|
+
makeMatch(sourceSchema, targetSchema, sourceTask.config.id, targetTask.config.id, ([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
|
|
2188
|
+
return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
|
|
2189
|
+
});
|
|
2190
|
+
const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
|
|
2191
|
+
const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r));
|
|
2192
|
+
let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
|
|
2193
|
+
if (unmatchedRequired.length > 0 && earlierTasks.length > 0) {
|
|
2194
|
+
for (let i = 0;i < earlierTasks.length && unmatchedRequired.length > 0; i++) {
|
|
2195
|
+
const earlierTask = earlierTasks[i];
|
|
2196
|
+
const earlierOutputSchema = earlierTask.outputSchema();
|
|
2197
|
+
const makeMatchFromEarlier = (comparator) => {
|
|
2198
|
+
if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
|
|
2202
|
+
for (const requiredInputId of unmatchedRequired) {
|
|
2203
|
+
const toPortInputSchema = targetSchema.properties?.[requiredInputId];
|
|
2204
|
+
if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
|
|
2205
|
+
matches.set(requiredInputId, fromOutputPortId);
|
|
2206
|
+
graph.addDataflow(new Dataflow(earlierTask.config.id, fromOutputPortId, targetTask.config.id, requiredInputId));
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
};
|
|
2211
|
+
makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
|
|
2212
|
+
const outputPortIdMatch = fromOutputPortId === toInputPortId;
|
|
2213
|
+
const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
|
|
2214
|
+
const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
|
|
2215
|
+
return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
|
|
2216
|
+
});
|
|
2217
|
+
makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
|
|
2218
|
+
return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
|
|
2219
|
+
});
|
|
2220
|
+
unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
|
|
2224
|
+
if (stillUnmatchedRequired.length > 0) {
|
|
2225
|
+
return {
|
|
2226
|
+
matches,
|
|
2227
|
+
error: `Could not find matches for required inputs [${stillUnmatchedRequired.join(", ")}] of ${targetTask.type}. ` + `Attempted to match from ${sourceTask.type} and earlier tasks.`,
|
|
2228
|
+
unmatchedRequired: stillUnmatchedRequired
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
|
|
2232
|
+
const hasRequiredInputs = requiredInputs.size > 0;
|
|
2233
|
+
const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
|
|
2234
|
+
const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
|
|
2235
|
+
if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
|
|
2236
|
+
return {
|
|
2237
|
+
matches,
|
|
2238
|
+
error: `Could not find a match between the outputs of ${sourceTask.type} and the inputs of ${targetTask.type}. ` + `You may need to connect the outputs to the inputs via connect() manually.`,
|
|
2239
|
+
unmatchedRequired: []
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
return {
|
|
2244
|
+
matches,
|
|
2245
|
+
unmatchedRequired: []
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
finalizeTemplate() {
|
|
2249
|
+
if (this._iteratorTask && this.graph.getTasks().length > 0) {
|
|
2250
|
+
this._iteratorTask.setTemplateGraph(this.graph);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
finalizeAndReturn() {
|
|
2254
|
+
if (!this._parentWorkflow) {
|
|
2255
|
+
throw new WorkflowError("finalizeAndReturn() can only be called on loop workflows");
|
|
2256
|
+
}
|
|
2257
|
+
this.finalizeTemplate();
|
|
2258
|
+
return this._parentWorkflow;
|
|
2259
|
+
}
|
|
1936
2260
|
}
|
|
1937
2261
|
|
|
1938
2262
|
// src/task-graph/Conversions.ts
|
|
@@ -2055,6 +2379,9 @@ function parallel(args, mergeFn = PROPERTY_ARRAY, workflow = new Workflow) {
|
|
|
2055
2379
|
return workflow;
|
|
2056
2380
|
}
|
|
2057
2381
|
|
|
2382
|
+
// src/task-graph/TaskGraph.ts
|
|
2383
|
+
init_Dataflow();
|
|
2384
|
+
|
|
2058
2385
|
// src/task-graph/TaskGraphEvents.ts
|
|
2059
2386
|
var EventDagToTaskGraphMapping = {
|
|
2060
2387
|
"node-added": "task_added",
|
|
@@ -2097,7 +2424,6 @@ class TaskGraph {
|
|
|
2097
2424
|
run(input = {}, config = {}) {
|
|
2098
2425
|
return this.runner.runGraph(input, {
|
|
2099
2426
|
outputCache: config?.outputCache || this.outputCache,
|
|
2100
|
-
parentProvenance: config?.parentProvenance || {},
|
|
2101
2427
|
parentSignal: config?.parentSignal || undefined
|
|
2102
2428
|
});
|
|
2103
2429
|
}
|
|
@@ -2173,7 +2499,7 @@ class TaskGraph {
|
|
|
2173
2499
|
return this._dag.removeNode(taskId);
|
|
2174
2500
|
}
|
|
2175
2501
|
resetGraph() {
|
|
2176
|
-
this.runner.resetGraph(this,
|
|
2502
|
+
this.runner.resetGraph(this, uuid44());
|
|
2177
2503
|
}
|
|
2178
2504
|
toJSON() {
|
|
2179
2505
|
const tasks = this.getTasks().map((node) => node.toJSON());
|
|
@@ -2338,117 +2664,456 @@ function serialGraph(tasks, inputHandle, outputHandle) {
|
|
|
2338
2664
|
graph.addDataflows(serialGraphEdges(tasks, inputHandle, outputHandle));
|
|
2339
2665
|
return graph;
|
|
2340
2666
|
}
|
|
2667
|
+
// src/task/IteratorTaskRunner.ts
|
|
2668
|
+
import { Job, JobQueueClient, JobQueueServer } from "@workglow/job-queue";
|
|
2669
|
+
import { InMemoryQueueStorage } from "@workglow/storage";
|
|
2670
|
+
import { uuid4 as uuid45 } from "@workglow/util";
|
|
2341
2671
|
|
|
2342
|
-
// src/task/
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
}
|
|
2352
|
-
regenerateGraph() {
|
|
2353
|
-
const arrayInputs = new Map;
|
|
2354
|
-
let hasArrayInputs = false;
|
|
2355
|
-
const inputSchema = this.inputSchema();
|
|
2356
|
-
if (typeof inputSchema !== "boolean") {
|
|
2357
|
-
const keys = Object.keys(inputSchema.properties || {});
|
|
2358
|
-
for (const inputId of keys) {
|
|
2359
|
-
const inputValue = this.runInputData[inputId];
|
|
2360
|
-
const inputDef = inputSchema.properties?.[inputId];
|
|
2361
|
-
if (typeof inputDef === "object" && inputDef !== null && "x-replicate" in inputDef && inputDef["x-replicate"] === true && Array.isArray(inputValue) && inputValue.length > 1) {
|
|
2362
|
-
arrayInputs.set(inputId, inputValue);
|
|
2363
|
-
hasArrayInputs = true;
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
this.subGraph = new TaskGraph;
|
|
2368
|
-
if (!hasArrayInputs) {
|
|
2369
|
-
super.regenerateGraph();
|
|
2370
|
-
return;
|
|
2672
|
+
// src/task/TaskQueueRegistry.ts
|
|
2673
|
+
var taskQueueRegistry = null;
|
|
2674
|
+
|
|
2675
|
+
class TaskQueueRegistry {
|
|
2676
|
+
queues = new Map;
|
|
2677
|
+
registerQueue(queue) {
|
|
2678
|
+
const queueName = queue.server.queueName;
|
|
2679
|
+
if (this.queues.has(queueName)) {
|
|
2680
|
+
throw new Error(`Queue with name ${queueName} already exists`);
|
|
2371
2681
|
}
|
|
2372
|
-
|
|
2373
|
-
const inputObject = Object.fromEntries(arrayInputs);
|
|
2374
|
-
const combinations = this.generateCombinations(inputObject, inputIds);
|
|
2375
|
-
const tasks = combinations.map((combination) => {
|
|
2376
|
-
const { id, name, ...rest } = this.config;
|
|
2377
|
-
const task = new this.constructor({ ...this.defaults, ...this.runInputData, ...combination }, { ...rest, id: `${id}_${uuid44()}` });
|
|
2378
|
-
return task;
|
|
2379
|
-
});
|
|
2380
|
-
this.subGraph.addTasks(tasks);
|
|
2381
|
-
super.regenerateGraph();
|
|
2682
|
+
this.queues.set(queueName, queue);
|
|
2382
2683
|
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
const indices = arraysToCombine.map(() => 0);
|
|
2386
|
-
const combinations = [];
|
|
2387
|
-
let done = false;
|
|
2388
|
-
while (!done) {
|
|
2389
|
-
combinations.push([...indices]);
|
|
2390
|
-
for (let i = indices.length - 1;i >= 0; i--) {
|
|
2391
|
-
if (++indices[i] < arraysToCombine[i].length)
|
|
2392
|
-
break;
|
|
2393
|
-
if (i === 0)
|
|
2394
|
-
done = true;
|
|
2395
|
-
else
|
|
2396
|
-
indices[i] = 0;
|
|
2397
|
-
}
|
|
2398
|
-
}
|
|
2399
|
-
const combos = combinations.map((combination) => {
|
|
2400
|
-
const result = { ...input };
|
|
2401
|
-
combination.forEach((valueIndex, arrayIndex) => {
|
|
2402
|
-
const key = inputMakeArray[arrayIndex];
|
|
2403
|
-
if (Array.isArray(input[key]))
|
|
2404
|
-
result[key] = input[key][valueIndex];
|
|
2405
|
-
});
|
|
2406
|
-
return result;
|
|
2407
|
-
});
|
|
2408
|
-
return combos;
|
|
2684
|
+
getQueue(queueName) {
|
|
2685
|
+
return this.queues.get(queueName);
|
|
2409
2686
|
}
|
|
2410
|
-
|
|
2411
|
-
const
|
|
2412
|
-
|
|
2687
|
+
startQueues() {
|
|
2688
|
+
for (const queue of this.queues.values()) {
|
|
2689
|
+
queue.server.start();
|
|
2690
|
+
}
|
|
2691
|
+
return this;
|
|
2413
2692
|
}
|
|
2414
|
-
|
|
2415
|
-
const
|
|
2416
|
-
|
|
2693
|
+
stopQueues() {
|
|
2694
|
+
for (const queue of this.queues.values()) {
|
|
2695
|
+
queue.server.stop();
|
|
2696
|
+
}
|
|
2697
|
+
return this;
|
|
2417
2698
|
}
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2699
|
+
clearQueues() {
|
|
2700
|
+
for (const queue of this.queues.values()) {
|
|
2701
|
+
queue.storage.deleteAll();
|
|
2421
2702
|
}
|
|
2422
|
-
return this
|
|
2703
|
+
return this;
|
|
2423
2704
|
}
|
|
2424
2705
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
return super.executeTaskChildren({});
|
|
2706
|
+
function getTaskQueueRegistry() {
|
|
2707
|
+
if (!taskQueueRegistry) {
|
|
2708
|
+
taskQueueRegistry = new TaskQueueRegistry;
|
|
2429
2709
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
return this.task.runOutputData;
|
|
2437
|
-
}
|
|
2710
|
+
return taskQueueRegistry;
|
|
2711
|
+
}
|
|
2712
|
+
function setTaskQueueRegistry(registry) {
|
|
2713
|
+
if (taskQueueRegistry) {
|
|
2714
|
+
taskQueueRegistry.stopQueues();
|
|
2715
|
+
taskQueueRegistry.clearQueues();
|
|
2438
2716
|
}
|
|
2717
|
+
taskQueueRegistry = registry;
|
|
2439
2718
|
}
|
|
2440
|
-
// src/task/InputTask.ts
|
|
2441
|
-
import { Task as Task2 } from "@workglow/task-graph";
|
|
2442
2719
|
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2720
|
+
// src/task/IteratorTaskRunner.ts
|
|
2721
|
+
class IteratorTaskRunner extends GraphAsTaskRunner {
|
|
2722
|
+
iteratorQueue;
|
|
2723
|
+
iteratorQueueName;
|
|
2724
|
+
async getOrCreateIteratorQueue() {
|
|
2725
|
+
const executionMode = this.task.executionMode;
|
|
2726
|
+
if (executionMode === "parallel") {
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
if (this.iteratorQueue) {
|
|
2730
|
+
return this.iteratorQueue;
|
|
2731
|
+
}
|
|
2732
|
+
const queueName = this.task.config.queueName ?? `iterator-${this.task.config.id}-${uuid45().slice(0, 8)}`;
|
|
2733
|
+
this.iteratorQueueName = queueName;
|
|
2734
|
+
const existingQueue = getTaskQueueRegistry().getQueue(queueName);
|
|
2735
|
+
if (existingQueue) {
|
|
2736
|
+
this.iteratorQueue = existingQueue;
|
|
2737
|
+
return existingQueue;
|
|
2738
|
+
}
|
|
2739
|
+
const concurrency = this.getConcurrencyForMode(executionMode);
|
|
2740
|
+
this.iteratorQueue = await this.createIteratorQueue(queueName, concurrency);
|
|
2741
|
+
return this.iteratorQueue;
|
|
2742
|
+
}
|
|
2743
|
+
getConcurrencyForMode(mode) {
|
|
2744
|
+
switch (mode) {
|
|
2745
|
+
case "sequential":
|
|
2746
|
+
return 1;
|
|
2747
|
+
case "parallel-limited":
|
|
2748
|
+
return this.task.concurrencyLimit;
|
|
2749
|
+
case "batched":
|
|
2750
|
+
return this.task.batchSize;
|
|
2751
|
+
case "parallel":
|
|
2752
|
+
default:
|
|
2753
|
+
return Infinity;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
async createIteratorQueue(queueName, concurrency) {
|
|
2757
|
+
const storage = new InMemoryQueueStorage(queueName);
|
|
2758
|
+
await storage.setupDatabase();
|
|
2759
|
+
const JobClass = class extends Job {
|
|
2760
|
+
async execute(input) {
|
|
2761
|
+
return input;
|
|
2762
|
+
}
|
|
2763
|
+
};
|
|
2764
|
+
const server = new JobQueueServer(JobClass, {
|
|
2765
|
+
storage,
|
|
2766
|
+
queueName,
|
|
2767
|
+
workerCount: Math.min(concurrency, 10)
|
|
2768
|
+
});
|
|
2769
|
+
const client = new JobQueueClient({
|
|
2770
|
+
storage,
|
|
2771
|
+
queueName
|
|
2772
|
+
});
|
|
2773
|
+
client.attach(server);
|
|
2774
|
+
const queue = {
|
|
2775
|
+
server,
|
|
2776
|
+
client,
|
|
2777
|
+
storage
|
|
2778
|
+
};
|
|
2779
|
+
try {
|
|
2780
|
+
getTaskQueueRegistry().registerQueue(queue);
|
|
2781
|
+
} catch (err) {
|
|
2782
|
+
const existing = getTaskQueueRegistry().getQueue(queueName);
|
|
2783
|
+
if (existing) {
|
|
2784
|
+
return existing;
|
|
2785
|
+
}
|
|
2786
|
+
throw err;
|
|
2787
|
+
}
|
|
2788
|
+
await server.start();
|
|
2789
|
+
return queue;
|
|
2790
|
+
}
|
|
2791
|
+
async executeTaskChildren(input) {
|
|
2792
|
+
const executionMode = this.task.executionMode;
|
|
2793
|
+
switch (executionMode) {
|
|
2794
|
+
case "sequential":
|
|
2795
|
+
return this.executeSequential(input);
|
|
2796
|
+
case "parallel-limited":
|
|
2797
|
+
return this.executeParallelLimited(input);
|
|
2798
|
+
case "batched":
|
|
2799
|
+
return this.executeBatched(input);
|
|
2800
|
+
case "parallel":
|
|
2801
|
+
default:
|
|
2802
|
+
return super.executeTaskChildren(input);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
async executeSequential(input) {
|
|
2806
|
+
const tasks = this.task.subGraph.getTasks();
|
|
2807
|
+
const results = [];
|
|
2808
|
+
for (const task of tasks) {
|
|
2809
|
+
if (this.abortController?.signal.aborted) {
|
|
2810
|
+
break;
|
|
2811
|
+
}
|
|
2812
|
+
const taskResult = await task.run(input);
|
|
2813
|
+
results.push({
|
|
2814
|
+
id: task.config.id,
|
|
2815
|
+
type: task.type,
|
|
2816
|
+
data: taskResult
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
return results;
|
|
2820
|
+
}
|
|
2821
|
+
async executeParallelLimited(input) {
|
|
2822
|
+
const tasks = this.task.subGraph.getTasks();
|
|
2823
|
+
const results = [];
|
|
2824
|
+
const limit = this.task.concurrencyLimit;
|
|
2825
|
+
for (let i = 0;i < tasks.length; i += limit) {
|
|
2826
|
+
if (this.abortController?.signal.aborted) {
|
|
2827
|
+
break;
|
|
2828
|
+
}
|
|
2829
|
+
const chunk = tasks.slice(i, i + limit);
|
|
2830
|
+
const chunkPromises = chunk.map(async (task) => {
|
|
2831
|
+
const taskResult = await task.run(input);
|
|
2832
|
+
return {
|
|
2833
|
+
id: task.config.id,
|
|
2834
|
+
type: task.type,
|
|
2835
|
+
data: taskResult
|
|
2836
|
+
};
|
|
2837
|
+
});
|
|
2838
|
+
const chunkResults = await Promise.all(chunkPromises);
|
|
2839
|
+
results.push(...chunkResults);
|
|
2840
|
+
}
|
|
2841
|
+
return results;
|
|
2842
|
+
}
|
|
2843
|
+
async executeBatched(input) {
|
|
2844
|
+
const tasks = this.task.subGraph.getTasks();
|
|
2845
|
+
const results = [];
|
|
2846
|
+
const batchSize = this.task.batchSize;
|
|
2847
|
+
for (let i = 0;i < tasks.length; i += batchSize) {
|
|
2848
|
+
if (this.abortController?.signal.aborted) {
|
|
2849
|
+
break;
|
|
2850
|
+
}
|
|
2851
|
+
const batch = tasks.slice(i, i + batchSize);
|
|
2852
|
+
const batchPromises = batch.map(async (task) => {
|
|
2853
|
+
const taskResult = await task.run(input);
|
|
2854
|
+
return {
|
|
2855
|
+
id: task.config.id,
|
|
2856
|
+
type: task.type,
|
|
2857
|
+
data: taskResult
|
|
2858
|
+
};
|
|
2859
|
+
});
|
|
2860
|
+
const batchResults = await Promise.all(batchPromises);
|
|
2861
|
+
results.push(...batchResults);
|
|
2862
|
+
const progress = Math.round((i + batch.length) / tasks.length * 100);
|
|
2863
|
+
this.task.emit("progress", progress, `Completed batch ${Math.ceil((i + 1) / batchSize)}`);
|
|
2864
|
+
}
|
|
2865
|
+
return results;
|
|
2866
|
+
}
|
|
2867
|
+
async cleanup() {
|
|
2868
|
+
if (this.iteratorQueue && this.iteratorQueueName) {
|
|
2869
|
+
try {
|
|
2870
|
+
this.iteratorQueue.server.stop();
|
|
2871
|
+
} catch (err) {}
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
// src/task/IteratorTask.ts
|
|
2877
|
+
class IteratorTask extends GraphAsTask {
|
|
2878
|
+
static type = "IteratorTask";
|
|
2879
|
+
static category = "Flow Control";
|
|
2880
|
+
static title = "Iterator";
|
|
2881
|
+
static description = "Base class for loop-type tasks";
|
|
2882
|
+
static hasDynamicSchemas = true;
|
|
2883
|
+
_templateGraph;
|
|
2884
|
+
_iteratorPortInfo;
|
|
2885
|
+
constructor(input = {}, config = {}) {
|
|
2886
|
+
super(input, config);
|
|
2887
|
+
}
|
|
2888
|
+
get runner() {
|
|
2889
|
+
if (!this._runner) {
|
|
2890
|
+
this._runner = new IteratorTaskRunner(this);
|
|
2891
|
+
}
|
|
2892
|
+
return this._runner;
|
|
2893
|
+
}
|
|
2894
|
+
get executionMode() {
|
|
2895
|
+
return this.config.executionMode ?? "parallel";
|
|
2896
|
+
}
|
|
2897
|
+
get concurrencyLimit() {
|
|
2898
|
+
return this.config.concurrencyLimit ?? 5;
|
|
2899
|
+
}
|
|
2900
|
+
get batchSize() {
|
|
2901
|
+
return this.config.batchSize ?? 10;
|
|
2902
|
+
}
|
|
2903
|
+
detectIteratorPort() {
|
|
2904
|
+
if (this._iteratorPortInfo) {
|
|
2905
|
+
return this._iteratorPortInfo;
|
|
2906
|
+
}
|
|
2907
|
+
if (this.config.iteratorPort) {
|
|
2908
|
+
const schema2 = this.inputSchema();
|
|
2909
|
+
if (typeof schema2 === "boolean")
|
|
2910
|
+
return;
|
|
2911
|
+
const portSchema = schema2.properties?.[this.config.iteratorPort];
|
|
2912
|
+
if (portSchema && typeof portSchema === "object") {
|
|
2913
|
+
const itemSchema = portSchema.items ?? { type: "object" };
|
|
2914
|
+
this._iteratorPortInfo = {
|
|
2915
|
+
portName: this.config.iteratorPort,
|
|
2916
|
+
itemSchema
|
|
2917
|
+
};
|
|
2918
|
+
return this._iteratorPortInfo;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
const schema = this.inputSchema();
|
|
2922
|
+
if (typeof schema === "boolean")
|
|
2923
|
+
return;
|
|
2924
|
+
const properties = schema.properties || {};
|
|
2925
|
+
for (const [portName, portSchema] of Object.entries(properties)) {
|
|
2926
|
+
if (typeof portSchema !== "object" || portSchema === null)
|
|
2927
|
+
continue;
|
|
2928
|
+
const ps = portSchema;
|
|
2929
|
+
if (ps.type === "array" || ps.items !== undefined) {
|
|
2930
|
+
const itemSchema = ps.items ?? {
|
|
2931
|
+
type: "object",
|
|
2932
|
+
properties: {},
|
|
2933
|
+
additionalProperties: true
|
|
2934
|
+
};
|
|
2935
|
+
this._iteratorPortInfo = { portName, itemSchema };
|
|
2936
|
+
return this._iteratorPortInfo;
|
|
2937
|
+
}
|
|
2938
|
+
const variants = ps.oneOf ?? ps.anyOf;
|
|
2939
|
+
if (Array.isArray(variants)) {
|
|
2940
|
+
for (const variant of variants) {
|
|
2941
|
+
if (typeof variant === "object" && variant !== null) {
|
|
2942
|
+
const v = variant;
|
|
2943
|
+
if (v.type === "array" || v.items !== undefined) {
|
|
2944
|
+
const itemSchema = v.items ?? {
|
|
2945
|
+
type: "object",
|
|
2946
|
+
properties: {},
|
|
2947
|
+
additionalProperties: true
|
|
2948
|
+
};
|
|
2949
|
+
this._iteratorPortInfo = { portName, itemSchema };
|
|
2950
|
+
return this._iteratorPortInfo;
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
getIteratorPortName() {
|
|
2959
|
+
return this.detectIteratorPort()?.portName;
|
|
2960
|
+
}
|
|
2961
|
+
getItemSchema() {
|
|
2962
|
+
return this.detectIteratorPort()?.itemSchema ?? {
|
|
2963
|
+
type: "object",
|
|
2964
|
+
properties: {},
|
|
2965
|
+
additionalProperties: true
|
|
2966
|
+
};
|
|
2967
|
+
}
|
|
2968
|
+
getIterableItems(input) {
|
|
2969
|
+
const portName = this.getIteratorPortName();
|
|
2970
|
+
if (!portName) {
|
|
2971
|
+
throw new TaskConfigurationError(`${this.type}: No array port found in input schema. ` + `Specify 'iteratorPort' in config or ensure input has an array-typed property.`);
|
|
2972
|
+
}
|
|
2973
|
+
const items = input[portName];
|
|
2974
|
+
if (items === undefined || items === null) {
|
|
2975
|
+
return [];
|
|
2976
|
+
}
|
|
2977
|
+
if (Array.isArray(items)) {
|
|
2978
|
+
return items;
|
|
2979
|
+
}
|
|
2980
|
+
return [items];
|
|
2981
|
+
}
|
|
2982
|
+
setTemplateGraph(graph) {
|
|
2983
|
+
this._templateGraph = graph;
|
|
2984
|
+
this.events.emit("regenerate");
|
|
2985
|
+
}
|
|
2986
|
+
getTemplateGraph() {
|
|
2987
|
+
return this._templateGraph;
|
|
2988
|
+
}
|
|
2989
|
+
regenerateGraph() {
|
|
2990
|
+
this.subGraph = new TaskGraph;
|
|
2991
|
+
if (!this._templateGraph || !this._templateGraph.getTasks().length) {
|
|
2992
|
+
super.regenerateGraph();
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
const items = this.getIterableItems(this.runInputData);
|
|
2996
|
+
if (items.length === 0) {
|
|
2997
|
+
super.regenerateGraph();
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
this.createIterationTasks(items);
|
|
3001
|
+
super.regenerateGraph();
|
|
3002
|
+
}
|
|
3003
|
+
createIterationTasks(items) {
|
|
3004
|
+
const portName = this.getIteratorPortName();
|
|
3005
|
+
if (!portName)
|
|
3006
|
+
return;
|
|
3007
|
+
const baseInput = {};
|
|
3008
|
+
for (const [key, value] of Object.entries(this.runInputData)) {
|
|
3009
|
+
if (key !== portName) {
|
|
3010
|
+
baseInput[key] = value;
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
for (let i = 0;i < items.length; i++) {
|
|
3014
|
+
const item = items[i];
|
|
3015
|
+
const iterationInput = {
|
|
3016
|
+
...baseInput,
|
|
3017
|
+
[portName]: item,
|
|
3018
|
+
_iterationIndex: i,
|
|
3019
|
+
_iterationItem: item
|
|
3020
|
+
};
|
|
3021
|
+
this.cloneTemplateForIteration(iterationInput, i);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
cloneTemplateForIteration(iterationInput, index) {
|
|
3025
|
+
if (!this._templateGraph)
|
|
3026
|
+
return;
|
|
3027
|
+
const templateTasks = this._templateGraph.getTasks();
|
|
3028
|
+
const templateDataflows = this._templateGraph.getDataflows();
|
|
3029
|
+
const idMap = new Map;
|
|
3030
|
+
for (const templateTask of templateTasks) {
|
|
3031
|
+
const TaskClass = templateTask.constructor;
|
|
3032
|
+
const clonedTask = new TaskClass({ ...templateTask.defaults, ...iterationInput }, {
|
|
3033
|
+
...templateTask.config,
|
|
3034
|
+
id: `${templateTask.config.id}_iter${index}`,
|
|
3035
|
+
name: `${templateTask.config.name || templateTask.type} [${index}]`
|
|
3036
|
+
});
|
|
3037
|
+
this.subGraph.addTask(clonedTask);
|
|
3038
|
+
idMap.set(templateTask.config.id, clonedTask.config.id);
|
|
3039
|
+
}
|
|
3040
|
+
for (const templateDataflow of templateDataflows) {
|
|
3041
|
+
const sourceId = idMap.get(templateDataflow.sourceTaskId);
|
|
3042
|
+
const targetId = idMap.get(templateDataflow.targetTaskId);
|
|
3043
|
+
if (sourceId !== undefined && targetId !== undefined) {
|
|
3044
|
+
const { Dataflow: Dataflow2 } = (init_Dataflow(), __toCommonJS(exports_Dataflow));
|
|
3045
|
+
const clonedDataflow = new Dataflow2(sourceId, templateDataflow.sourceTaskPortId, targetId, templateDataflow.targetTaskPortId);
|
|
3046
|
+
this.subGraph.addDataflow(clonedDataflow);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
async execute(input, context) {
|
|
3051
|
+
const items = this.getIterableItems(input);
|
|
3052
|
+
if (items.length === 0) {
|
|
3053
|
+
return this.getEmptyResult();
|
|
3054
|
+
}
|
|
3055
|
+
this.runInputData = { ...this.defaults, ...input };
|
|
3056
|
+
this.regenerateGraph();
|
|
3057
|
+
return super.execute(input, context);
|
|
3058
|
+
}
|
|
3059
|
+
getEmptyResult() {
|
|
3060
|
+
return {};
|
|
3061
|
+
}
|
|
3062
|
+
collectResults(results) {
|
|
3063
|
+
return results;
|
|
3064
|
+
}
|
|
3065
|
+
inputSchema() {
|
|
3066
|
+
if (this.hasChildren() || this._templateGraph) {
|
|
3067
|
+
return super.inputSchema();
|
|
3068
|
+
}
|
|
3069
|
+
return this.constructor.inputSchema();
|
|
3070
|
+
}
|
|
3071
|
+
outputSchema() {
|
|
3072
|
+
if (!this.hasChildren() && !this._templateGraph) {
|
|
3073
|
+
return this.constructor.outputSchema();
|
|
3074
|
+
}
|
|
3075
|
+
return this.getWrappedOutputSchema();
|
|
3076
|
+
}
|
|
3077
|
+
getWrappedOutputSchema() {
|
|
3078
|
+
const templateGraph = this._templateGraph ?? this.subGraph;
|
|
3079
|
+
if (!templateGraph) {
|
|
3080
|
+
return { type: "object", properties: {}, additionalProperties: false };
|
|
3081
|
+
}
|
|
3082
|
+
const tasks = templateGraph.getTasks();
|
|
3083
|
+
const endingNodes = tasks.filter((task) => templateGraph.getTargetDataflows(task.config.id).length === 0);
|
|
3084
|
+
if (endingNodes.length === 0) {
|
|
3085
|
+
return { type: "object", properties: {}, additionalProperties: false };
|
|
3086
|
+
}
|
|
3087
|
+
const properties = {};
|
|
3088
|
+
for (const task of endingNodes) {
|
|
3089
|
+
const taskOutputSchema = task.outputSchema();
|
|
3090
|
+
if (typeof taskOutputSchema === "boolean")
|
|
3091
|
+
continue;
|
|
3092
|
+
const taskProperties = taskOutputSchema.properties || {};
|
|
3093
|
+
for (const [key, schema] of Object.entries(taskProperties)) {
|
|
3094
|
+
properties[key] = {
|
|
3095
|
+
type: "array",
|
|
3096
|
+
items: schema
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
return {
|
|
3101
|
+
type: "object",
|
|
3102
|
+
properties,
|
|
3103
|
+
additionalProperties: false
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
// src/task/BatchTask.ts
|
|
3109
|
+
class BatchTask extends IteratorTask {
|
|
3110
|
+
static type = "BatchTask";
|
|
3111
|
+
static category = "Flow Control";
|
|
3112
|
+
static title = "Batch";
|
|
3113
|
+
static description = "Processes an array in configurable batches";
|
|
3114
|
+
static compoundMerge = PROPERTY_ARRAY;
|
|
3115
|
+
static inputSchema() {
|
|
3116
|
+
return {
|
|
2452
3117
|
type: "object",
|
|
2453
3118
|
properties: {},
|
|
2454
3119
|
additionalProperties: true
|
|
@@ -2461,25 +3126,161 @@ class InputTask extends Task2 {
|
|
|
2461
3126
|
additionalProperties: true
|
|
2462
3127
|
};
|
|
2463
3128
|
}
|
|
2464
|
-
|
|
2465
|
-
return this.config
|
|
3129
|
+
get batchSize() {
|
|
3130
|
+
return this.config.batchSize ?? 10;
|
|
3131
|
+
}
|
|
3132
|
+
get flattenResults() {
|
|
3133
|
+
return this.config.flattenResults ?? true;
|
|
3134
|
+
}
|
|
3135
|
+
get batchExecutionMode() {
|
|
3136
|
+
return this.config.batchExecutionMode ?? "sequential";
|
|
3137
|
+
}
|
|
3138
|
+
getIterableItems(input) {
|
|
3139
|
+
const items = super.getIterableItems(input);
|
|
3140
|
+
return this.groupIntoBatches(items);
|
|
3141
|
+
}
|
|
3142
|
+
groupIntoBatches(items) {
|
|
3143
|
+
const batches = [];
|
|
3144
|
+
const size = this.batchSize;
|
|
3145
|
+
for (let i = 0;i < items.length; i += size) {
|
|
3146
|
+
batches.push(items.slice(i, i + size));
|
|
3147
|
+
}
|
|
3148
|
+
return batches;
|
|
3149
|
+
}
|
|
3150
|
+
createIterationTasks(batches) {
|
|
3151
|
+
const portName = this.getIteratorPortName();
|
|
3152
|
+
if (!portName)
|
|
3153
|
+
return;
|
|
3154
|
+
const baseInput = {};
|
|
3155
|
+
for (const [key, value] of Object.entries(this.runInputData)) {
|
|
3156
|
+
if (key !== portName) {
|
|
3157
|
+
baseInput[key] = value;
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
for (let i = 0;i < batches.length; i++) {
|
|
3161
|
+
const batch = batches[i];
|
|
3162
|
+
const batchInput = {
|
|
3163
|
+
...baseInput,
|
|
3164
|
+
[portName]: batch,
|
|
3165
|
+
_batchIndex: i,
|
|
3166
|
+
_batchItems: batch
|
|
3167
|
+
};
|
|
3168
|
+
this.cloneTemplateForIteration(batchInput, i);
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
getEmptyResult() {
|
|
3172
|
+
const schema = this.outputSchema();
|
|
3173
|
+
if (typeof schema === "boolean") {
|
|
3174
|
+
return {};
|
|
3175
|
+
}
|
|
3176
|
+
const result = {};
|
|
3177
|
+
for (const key of Object.keys(schema.properties || {})) {
|
|
3178
|
+
result[key] = [];
|
|
3179
|
+
}
|
|
3180
|
+
return result;
|
|
2466
3181
|
}
|
|
2467
3182
|
outputSchema() {
|
|
2468
|
-
|
|
3183
|
+
if (!this.hasChildren() && !this._templateGraph) {
|
|
3184
|
+
return this.constructor.outputSchema();
|
|
3185
|
+
}
|
|
3186
|
+
return this.getWrappedOutputSchema();
|
|
2469
3187
|
}
|
|
2470
|
-
|
|
2471
|
-
|
|
3188
|
+
collectResults(results) {
|
|
3189
|
+
const collected = super.collectResults(results);
|
|
3190
|
+
if (!this.flattenResults || typeof collected !== "object" || collected === null) {
|
|
3191
|
+
return collected;
|
|
3192
|
+
}
|
|
3193
|
+
const flattened = {};
|
|
3194
|
+
for (const [key, value] of Object.entries(collected)) {
|
|
3195
|
+
if (Array.isArray(value)) {
|
|
3196
|
+
flattened[key] = value.flat(2);
|
|
3197
|
+
} else {
|
|
3198
|
+
flattened[key] = value;
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
return flattened;
|
|
2472
3202
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
3203
|
+
regenerateGraph() {
|
|
3204
|
+
this.subGraph = new TaskGraph;
|
|
3205
|
+
if (!this._templateGraph || !this._templateGraph.getTasks().length) {
|
|
3206
|
+
super.regenerateGraph();
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
const batches = this.getIterableItems(this.runInputData);
|
|
3210
|
+
if (batches.length === 0) {
|
|
3211
|
+
super.regenerateGraph();
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
this.createIterationTasks(batches);
|
|
3215
|
+
this.events.emit("regenerate");
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
Workflow.prototype.batch = CreateLoopWorkflow(BatchTask);
|
|
3219
|
+
Workflow.prototype.endBatch = CreateEndLoopWorkflow("endBatch");
|
|
3220
|
+
// src/task/ForEachTask.ts
|
|
3221
|
+
class ForEachTask extends IteratorTask {
|
|
3222
|
+
static type = "ForEachTask";
|
|
3223
|
+
static category = "Flow Control";
|
|
3224
|
+
static title = "For Each";
|
|
3225
|
+
static description = "Iterates over an array and runs a workflow for each element";
|
|
3226
|
+
static inputSchema() {
|
|
3227
|
+
return {
|
|
3228
|
+
type: "object",
|
|
3229
|
+
properties: {},
|
|
3230
|
+
additionalProperties: true
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
static outputSchema() {
|
|
3234
|
+
return {
|
|
3235
|
+
type: "object",
|
|
3236
|
+
properties: {
|
|
3237
|
+
completed: {
|
|
3238
|
+
type: "boolean",
|
|
3239
|
+
title: "Completed",
|
|
3240
|
+
description: "Whether all iterations completed successfully"
|
|
3241
|
+
},
|
|
3242
|
+
count: {
|
|
3243
|
+
type: "number",
|
|
3244
|
+
title: "Count",
|
|
3245
|
+
description: "Number of items processed"
|
|
3246
|
+
}
|
|
3247
|
+
},
|
|
3248
|
+
additionalProperties: false
|
|
3249
|
+
};
|
|
3250
|
+
}
|
|
3251
|
+
get shouldCollectResults() {
|
|
3252
|
+
return this.config.shouldCollectResults ?? false;
|
|
3253
|
+
}
|
|
3254
|
+
getEmptyResult() {
|
|
3255
|
+
return {
|
|
3256
|
+
completed: true,
|
|
3257
|
+
count: 0
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
outputSchema() {
|
|
3261
|
+
if (this.shouldCollectResults && (this.hasChildren() || this._templateGraph)) {
|
|
3262
|
+
return this.getWrappedOutputSchema();
|
|
3263
|
+
}
|
|
3264
|
+
return this.constructor.outputSchema();
|
|
3265
|
+
}
|
|
3266
|
+
collectResults(results) {
|
|
3267
|
+
if (this.config.shouldCollectResults) {
|
|
3268
|
+
return super.collectResults(results);
|
|
3269
|
+
}
|
|
3270
|
+
return {
|
|
3271
|
+
completed: true,
|
|
3272
|
+
count: results.length
|
|
3273
|
+
};
|
|
2475
3274
|
}
|
|
2476
3275
|
}
|
|
3276
|
+
Workflow.prototype.forEach = CreateLoopWorkflow(ForEachTask);
|
|
3277
|
+
Workflow.prototype.endForEach = CreateEndLoopWorkflow("endForEach");
|
|
2477
3278
|
// src/task/JobQueueFactory.ts
|
|
2478
3279
|
import {
|
|
2479
|
-
JobQueueClient,
|
|
2480
|
-
JobQueueServer
|
|
3280
|
+
JobQueueClient as JobQueueClient2,
|
|
3281
|
+
JobQueueServer as JobQueueServer2
|
|
2481
3282
|
} from "@workglow/job-queue";
|
|
2482
|
-
import { InMemoryQueueStorage } from "@workglow/storage";
|
|
3283
|
+
import { InMemoryQueueStorage as InMemoryQueueStorage2 } from "@workglow/storage";
|
|
2483
3284
|
import { createServiceToken as createServiceToken2, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
|
|
2484
3285
|
var JOB_QUEUE_FACTORY = createServiceToken2("taskgraph.jobQueueFactory");
|
|
2485
3286
|
var defaultJobQueueFactory = async ({
|
|
@@ -2487,9 +3288,9 @@ var defaultJobQueueFactory = async ({
|
|
|
2487
3288
|
jobClass,
|
|
2488
3289
|
options
|
|
2489
3290
|
}) => {
|
|
2490
|
-
const storage = options?.storage ?? new
|
|
3291
|
+
const storage = options?.storage ?? new InMemoryQueueStorage2(queueName);
|
|
2491
3292
|
await storage.setupDatabase();
|
|
2492
|
-
const server = new
|
|
3293
|
+
const server = new JobQueueServer2(jobClass, {
|
|
2493
3294
|
storage,
|
|
2494
3295
|
queueName,
|
|
2495
3296
|
limiter: options?.limiter,
|
|
@@ -2500,7 +3301,7 @@ var defaultJobQueueFactory = async ({
|
|
|
2500
3301
|
deleteAfterDisabledMs: options?.deleteAfterDisabledMs,
|
|
2501
3302
|
cleanupIntervalMs: options?.cleanupIntervalMs
|
|
2502
3303
|
});
|
|
2503
|
-
const client = new
|
|
3304
|
+
const client = new JobQueueClient2({
|
|
2504
3305
|
storage,
|
|
2505
3306
|
queueName
|
|
2506
3307
|
});
|
|
@@ -2520,9 +3321,9 @@ function createJobQueueFactoryWithOptions(defaultOptions = {}) {
|
|
|
2520
3321
|
...defaultOptions,
|
|
2521
3322
|
...options ?? {}
|
|
2522
3323
|
};
|
|
2523
|
-
const storage = mergedOptions.storage ?? new
|
|
3324
|
+
const storage = mergedOptions.storage ?? new InMemoryQueueStorage2(queueName);
|
|
2524
3325
|
await storage.setupDatabase();
|
|
2525
|
-
const server = new
|
|
3326
|
+
const server = new JobQueueServer2(jobClass, {
|
|
2526
3327
|
storage,
|
|
2527
3328
|
queueName,
|
|
2528
3329
|
limiter: mergedOptions.limiter,
|
|
@@ -2533,7 +3334,7 @@ function createJobQueueFactoryWithOptions(defaultOptions = {}) {
|
|
|
2533
3334
|
deleteAfterDisabledMs: mergedOptions.deleteAfterDisabledMs,
|
|
2534
3335
|
cleanupIntervalMs: mergedOptions.cleanupIntervalMs
|
|
2535
3336
|
});
|
|
2536
|
-
const client = new
|
|
3337
|
+
const client = new JobQueueClient2({
|
|
2537
3338
|
storage,
|
|
2538
3339
|
queueName
|
|
2539
3340
|
});
|
|
@@ -2551,58 +3352,8 @@ if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
|
|
|
2551
3352
|
registerJobQueueFactory(defaultJobQueueFactory);
|
|
2552
3353
|
}
|
|
2553
3354
|
// src/task/JobQueueTask.ts
|
|
2554
|
-
import { Job as
|
|
2555
|
-
|
|
2556
|
-
// src/task/TaskQueueRegistry.ts
|
|
2557
|
-
var taskQueueRegistry = null;
|
|
2558
|
-
|
|
2559
|
-
class TaskQueueRegistry {
|
|
2560
|
-
queues = new Map;
|
|
2561
|
-
registerQueue(queue) {
|
|
2562
|
-
const queueName = queue.server.queueName;
|
|
2563
|
-
if (this.queues.has(queueName)) {
|
|
2564
|
-
throw new Error(`Queue with name ${queueName} already exists`);
|
|
2565
|
-
}
|
|
2566
|
-
this.queues.set(queueName, queue);
|
|
2567
|
-
}
|
|
2568
|
-
getQueue(queueName) {
|
|
2569
|
-
return this.queues.get(queueName);
|
|
2570
|
-
}
|
|
2571
|
-
startQueues() {
|
|
2572
|
-
for (const queue of this.queues.values()) {
|
|
2573
|
-
queue.server.start();
|
|
2574
|
-
}
|
|
2575
|
-
return this;
|
|
2576
|
-
}
|
|
2577
|
-
stopQueues() {
|
|
2578
|
-
for (const queue of this.queues.values()) {
|
|
2579
|
-
queue.server.stop();
|
|
2580
|
-
}
|
|
2581
|
-
return this;
|
|
2582
|
-
}
|
|
2583
|
-
clearQueues() {
|
|
2584
|
-
for (const queue of this.queues.values()) {
|
|
2585
|
-
queue.storage.deleteAll();
|
|
2586
|
-
}
|
|
2587
|
-
return this;
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
function getTaskQueueRegistry() {
|
|
2591
|
-
if (!taskQueueRegistry) {
|
|
2592
|
-
taskQueueRegistry = new TaskQueueRegistry;
|
|
2593
|
-
}
|
|
2594
|
-
return taskQueueRegistry;
|
|
2595
|
-
}
|
|
2596
|
-
function setTaskQueueRegistry(registry) {
|
|
2597
|
-
if (taskQueueRegistry) {
|
|
2598
|
-
taskQueueRegistry.stopQueues();
|
|
2599
|
-
taskQueueRegistry.clearQueues();
|
|
2600
|
-
}
|
|
2601
|
-
taskQueueRegistry = registry;
|
|
2602
|
-
}
|
|
2603
|
-
|
|
2604
|
-
// src/task/JobQueueTask.ts
|
|
2605
|
-
class JobQueueTask extends ArrayTask {
|
|
3355
|
+
import { Job as Job3 } from "@workglow/job-queue";
|
|
3356
|
+
class JobQueueTask extends GraphAsTask {
|
|
2606
3357
|
static type = "JobQueueTask";
|
|
2607
3358
|
static canRunDirectly = true;
|
|
2608
3359
|
currentQueueName;
|
|
@@ -2612,7 +3363,7 @@ class JobQueueTask extends ArrayTask {
|
|
|
2612
3363
|
constructor(input = {}, config = {}) {
|
|
2613
3364
|
config.queue ??= true;
|
|
2614
3365
|
super(input, config);
|
|
2615
|
-
this.jobClass =
|
|
3366
|
+
this.jobClass = Job3;
|
|
2616
3367
|
}
|
|
2617
3368
|
async execute(input, executeContext) {
|
|
2618
3369
|
let cleanup = () => {};
|
|
@@ -2734,16 +3485,13 @@ class JobQueueTask extends ArrayTask {
|
|
|
2734
3485
|
super.abort();
|
|
2735
3486
|
}
|
|
2736
3487
|
}
|
|
2737
|
-
// src/task/
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
class OutputTask extends Task3 {
|
|
2741
|
-
static type = "OutputTask";
|
|
3488
|
+
// src/task/MapTask.ts
|
|
3489
|
+
class MapTask extends IteratorTask {
|
|
3490
|
+
static type = "MapTask";
|
|
2742
3491
|
static category = "Flow Control";
|
|
2743
|
-
static title = "
|
|
2744
|
-
static description = "
|
|
2745
|
-
static
|
|
2746
|
-
static cacheable = false;
|
|
3492
|
+
static title = "Map";
|
|
3493
|
+
static description = "Transforms an array by running a workflow for each element";
|
|
3494
|
+
static compoundMerge = PROPERTY_ARRAY;
|
|
2747
3495
|
static inputSchema() {
|
|
2748
3496
|
return {
|
|
2749
3497
|
type: "object",
|
|
@@ -2758,19 +3506,198 @@ class OutputTask extends Task3 {
|
|
|
2758
3506
|
additionalProperties: true
|
|
2759
3507
|
};
|
|
2760
3508
|
}
|
|
2761
|
-
|
|
2762
|
-
return this.config
|
|
3509
|
+
get preserveOrder() {
|
|
3510
|
+
return this.config.preserveOrder ?? true;
|
|
3511
|
+
}
|
|
3512
|
+
get flatten() {
|
|
3513
|
+
return this.config.flatten ?? false;
|
|
3514
|
+
}
|
|
3515
|
+
getEmptyResult() {
|
|
3516
|
+
const schema = this.outputSchema();
|
|
3517
|
+
if (typeof schema === "boolean") {
|
|
3518
|
+
return {};
|
|
3519
|
+
}
|
|
3520
|
+
const result = {};
|
|
3521
|
+
for (const key of Object.keys(schema.properties || {})) {
|
|
3522
|
+
result[key] = [];
|
|
3523
|
+
}
|
|
3524
|
+
return result;
|
|
2763
3525
|
}
|
|
2764
3526
|
outputSchema() {
|
|
2765
|
-
|
|
3527
|
+
if (!this.hasChildren() && !this._templateGraph) {
|
|
3528
|
+
return this.constructor.outputSchema();
|
|
3529
|
+
}
|
|
3530
|
+
return this.getWrappedOutputSchema();
|
|
2766
3531
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
3532
|
+
collectResults(results) {
|
|
3533
|
+
const collected = super.collectResults(results);
|
|
3534
|
+
if (!this.flatten || typeof collected !== "object" || collected === null) {
|
|
3535
|
+
return collected;
|
|
3536
|
+
}
|
|
3537
|
+
const flattened = {};
|
|
3538
|
+
for (const [key, value] of Object.entries(collected)) {
|
|
3539
|
+
if (Array.isArray(value)) {
|
|
3540
|
+
flattened[key] = value.flat();
|
|
3541
|
+
} else {
|
|
3542
|
+
flattened[key] = value;
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
return flattened;
|
|
2769
3546
|
}
|
|
2770
|
-
|
|
2771
|
-
|
|
3547
|
+
}
|
|
3548
|
+
Workflow.prototype.map = CreateLoopWorkflow(MapTask);
|
|
3549
|
+
Workflow.prototype.endMap = CreateEndLoopWorkflow("endMap");
|
|
3550
|
+
// src/task/ReduceTask.ts
|
|
3551
|
+
class ReduceTask extends IteratorTask {
|
|
3552
|
+
static type = "ReduceTask";
|
|
3553
|
+
static category = "Flow Control";
|
|
3554
|
+
static title = "Reduce";
|
|
3555
|
+
static description = "Processes array elements sequentially with an accumulator (fold)";
|
|
3556
|
+
constructor(input = {}, config = {}) {
|
|
3557
|
+
const reduceConfig = {
|
|
3558
|
+
...config,
|
|
3559
|
+
executionMode: "sequential"
|
|
3560
|
+
};
|
|
3561
|
+
super(input, reduceConfig);
|
|
3562
|
+
}
|
|
3563
|
+
get initialValue() {
|
|
3564
|
+
return this.config.initialValue ?? {};
|
|
3565
|
+
}
|
|
3566
|
+
get accumulatorPort() {
|
|
3567
|
+
return this.config.accumulatorPort ?? "accumulator";
|
|
3568
|
+
}
|
|
3569
|
+
get currentItemPort() {
|
|
3570
|
+
return this.config.currentItemPort ?? "currentItem";
|
|
3571
|
+
}
|
|
3572
|
+
get indexPort() {
|
|
3573
|
+
return this.config.indexPort ?? "index";
|
|
3574
|
+
}
|
|
3575
|
+
async execute(input, context) {
|
|
3576
|
+
if (!this._templateGraph || this._templateGraph.getTasks().length === 0) {
|
|
3577
|
+
return this.initialValue;
|
|
3578
|
+
}
|
|
3579
|
+
const items = this.getIterableItems(input);
|
|
3580
|
+
if (items.length === 0) {
|
|
3581
|
+
return this.initialValue;
|
|
3582
|
+
}
|
|
3583
|
+
let accumulator = { ...this.initialValue };
|
|
3584
|
+
for (let index = 0;index < items.length; index++) {
|
|
3585
|
+
if (context.signal?.aborted) {
|
|
3586
|
+
break;
|
|
3587
|
+
}
|
|
3588
|
+
const currentItem = items[index];
|
|
3589
|
+
const stepInput = {
|
|
3590
|
+
...input,
|
|
3591
|
+
[this.accumulatorPort]: accumulator,
|
|
3592
|
+
[this.currentItemPort]: currentItem,
|
|
3593
|
+
[this.indexPort]: index
|
|
3594
|
+
};
|
|
3595
|
+
this.subGraph = this.cloneTemplateForStep(index);
|
|
3596
|
+
const results = await this.subGraph.run(stepInput, {
|
|
3597
|
+
parentSignal: context.signal
|
|
3598
|
+
});
|
|
3599
|
+
accumulator = this.subGraph.mergeExecuteOutputsToRunOutput(results, this.compoundMerge);
|
|
3600
|
+
const progress = Math.round((index + 1) / items.length * 100);
|
|
3601
|
+
await context.updateProgress(progress, `Processing item ${index + 1}/${items.length}`);
|
|
3602
|
+
}
|
|
3603
|
+
return accumulator;
|
|
3604
|
+
}
|
|
3605
|
+
getEmptyResult() {
|
|
3606
|
+
return this.initialValue;
|
|
3607
|
+
}
|
|
3608
|
+
cloneTemplateForStep(stepIndex) {
|
|
3609
|
+
const clonedGraph = new TaskGraph;
|
|
3610
|
+
if (!this._templateGraph) {
|
|
3611
|
+
return clonedGraph;
|
|
3612
|
+
}
|
|
3613
|
+
const templateTasks = this._templateGraph.getTasks();
|
|
3614
|
+
const templateDataflows = this._templateGraph.getDataflows();
|
|
3615
|
+
const idMap = new Map;
|
|
3616
|
+
for (const templateTask of templateTasks) {
|
|
3617
|
+
const TaskClass = templateTask.constructor;
|
|
3618
|
+
const clonedTask = new TaskClass({ ...templateTask.defaults }, {
|
|
3619
|
+
...templateTask.config,
|
|
3620
|
+
id: `${templateTask.config.id}_step${stepIndex}`,
|
|
3621
|
+
name: `${templateTask.config.name || templateTask.type} [${stepIndex}]`
|
|
3622
|
+
});
|
|
3623
|
+
clonedGraph.addTask(clonedTask);
|
|
3624
|
+
idMap.set(templateTask.config.id, clonedTask.config.id);
|
|
3625
|
+
}
|
|
3626
|
+
for (const templateDataflow of templateDataflows) {
|
|
3627
|
+
const sourceId = idMap.get(templateDataflow.sourceTaskId);
|
|
3628
|
+
const targetId = idMap.get(templateDataflow.targetTaskId);
|
|
3629
|
+
if (sourceId !== undefined && targetId !== undefined) {
|
|
3630
|
+
const { Dataflow: Dataflow2 } = (init_Dataflow(), __toCommonJS(exports_Dataflow));
|
|
3631
|
+
const clonedDataflow = new Dataflow2(sourceId, templateDataflow.sourceTaskPortId, targetId, templateDataflow.targetTaskPortId);
|
|
3632
|
+
clonedGraph.addDataflow(clonedDataflow);
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
return clonedGraph;
|
|
3636
|
+
}
|
|
3637
|
+
static inputSchema() {
|
|
3638
|
+
return {
|
|
3639
|
+
type: "object",
|
|
3640
|
+
properties: {
|
|
3641
|
+
accumulator: {
|
|
3642
|
+
title: "Accumulator",
|
|
3643
|
+
description: "The current accumulator value"
|
|
3644
|
+
},
|
|
3645
|
+
currentItem: {
|
|
3646
|
+
title: "Current Item",
|
|
3647
|
+
description: "The current item being processed"
|
|
3648
|
+
},
|
|
3649
|
+
index: {
|
|
3650
|
+
type: "number",
|
|
3651
|
+
title: "Index",
|
|
3652
|
+
description: "The current item index"
|
|
3653
|
+
}
|
|
3654
|
+
},
|
|
3655
|
+
additionalProperties: true
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
static outputSchema() {
|
|
3659
|
+
return {
|
|
3660
|
+
type: "object",
|
|
3661
|
+
properties: {},
|
|
3662
|
+
additionalProperties: true
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
outputSchema() {
|
|
3666
|
+
if (!this._templateGraph) {
|
|
3667
|
+
return this.constructor.outputSchema();
|
|
3668
|
+
}
|
|
3669
|
+
const tasks = this._templateGraph.getTasks();
|
|
3670
|
+
const endingNodes = tasks.filter((task) => this._templateGraph.getTargetDataflows(task.config.id).length === 0);
|
|
3671
|
+
if (endingNodes.length === 0) {
|
|
3672
|
+
return this.constructor.outputSchema();
|
|
3673
|
+
}
|
|
3674
|
+
const properties = {};
|
|
3675
|
+
for (const task of endingNodes) {
|
|
3676
|
+
const taskOutputSchema = task.outputSchema();
|
|
3677
|
+
if (typeof taskOutputSchema === "boolean")
|
|
3678
|
+
continue;
|
|
3679
|
+
const taskProperties = taskOutputSchema.properties || {};
|
|
3680
|
+
for (const [key, schema] of Object.entries(taskProperties)) {
|
|
3681
|
+
if (!properties[key]) {
|
|
3682
|
+
properties[key] = schema;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
return {
|
|
3687
|
+
type: "object",
|
|
3688
|
+
properties,
|
|
3689
|
+
additionalProperties: false
|
|
3690
|
+
};
|
|
3691
|
+
}
|
|
3692
|
+
regenerateGraph() {
|
|
3693
|
+
this.events.emit("regenerate");
|
|
2772
3694
|
}
|
|
2773
3695
|
}
|
|
3696
|
+
Workflow.prototype.reduce = CreateLoopWorkflow(ReduceTask);
|
|
3697
|
+
Workflow.prototype.endReduce = CreateEndLoopWorkflow("endReduce");
|
|
3698
|
+
// src/task/TaskJSON.ts
|
|
3699
|
+
init_Dataflow();
|
|
3700
|
+
|
|
2774
3701
|
// src/task/TaskRegistry.ts
|
|
2775
3702
|
var taskConstructors = new Map;
|
|
2776
3703
|
function registerTask(baseClass) {
|
|
@@ -2788,17 +3715,14 @@ var createSingleTaskFromJSON = (item) => {
|
|
|
2788
3715
|
throw new TaskJSONError("Task id required");
|
|
2789
3716
|
if (!item.type)
|
|
2790
3717
|
throw new TaskJSONError("Task type required");
|
|
2791
|
-
if (item.defaults &&
|
|
3718
|
+
if (item.defaults && Array.isArray(item.defaults))
|
|
2792
3719
|
throw new TaskJSONError("Task defaults must be an object");
|
|
2793
|
-
if (item.provenance && (Array.isArray(item.provenance) || typeof item.provenance !== "object"))
|
|
2794
|
-
throw new TaskJSONError("Task provenance must be an object");
|
|
2795
3720
|
const taskClass = TaskRegistry.all.get(item.type);
|
|
2796
3721
|
if (!taskClass)
|
|
2797
3722
|
throw new TaskJSONError(`Task type ${item.type} not found, perhaps not registered?`);
|
|
2798
3723
|
const taskConfig = {
|
|
2799
3724
|
id: item.id,
|
|
2800
3725
|
name: item.name,
|
|
2801
|
-
provenance: item.provenance ?? {},
|
|
2802
3726
|
extras: item.extras
|
|
2803
3727
|
};
|
|
2804
3728
|
const task = new taskClass(item.defaults ?? {}, taskConfig);
|
|
@@ -2841,6 +3765,170 @@ var createGraphFromGraphJSON = (graphJsonObj) => {
|
|
|
2841
3765
|
}
|
|
2842
3766
|
return subGraph;
|
|
2843
3767
|
};
|
|
3768
|
+
|
|
3769
|
+
// src/task/index.ts
|
|
3770
|
+
init_TaskTypes();
|
|
3771
|
+
|
|
3772
|
+
// src/task/WhileTask.ts
|
|
3773
|
+
class WhileTask extends GraphAsTask {
|
|
3774
|
+
static type = "WhileTask";
|
|
3775
|
+
static category = "Flow Control";
|
|
3776
|
+
static title = "While Loop";
|
|
3777
|
+
static description = "Loops until a condition function returns false";
|
|
3778
|
+
static hasDynamicSchemas = true;
|
|
3779
|
+
_templateGraph;
|
|
3780
|
+
_currentIteration = 0;
|
|
3781
|
+
constructor(input = {}, config = {}) {
|
|
3782
|
+
super(input, config);
|
|
3783
|
+
}
|
|
3784
|
+
get condition() {
|
|
3785
|
+
return this.config.condition;
|
|
3786
|
+
}
|
|
3787
|
+
get maxIterations() {
|
|
3788
|
+
return this.config.maxIterations ?? 100;
|
|
3789
|
+
}
|
|
3790
|
+
get chainIterations() {
|
|
3791
|
+
return this.config.chainIterations ?? true;
|
|
3792
|
+
}
|
|
3793
|
+
get currentIteration() {
|
|
3794
|
+
return this._currentIteration;
|
|
3795
|
+
}
|
|
3796
|
+
setTemplateGraph(graph) {
|
|
3797
|
+
this._templateGraph = graph;
|
|
3798
|
+
}
|
|
3799
|
+
getTemplateGraph() {
|
|
3800
|
+
return this._templateGraph;
|
|
3801
|
+
}
|
|
3802
|
+
async execute(input, context) {
|
|
3803
|
+
if (!this._templateGraph || this._templateGraph.getTasks().length === 0) {
|
|
3804
|
+
throw new TaskConfigurationError(`${this.type}: No template graph set for while loop`);
|
|
3805
|
+
}
|
|
3806
|
+
if (!this.condition) {
|
|
3807
|
+
throw new TaskConfigurationError(`${this.type}: No condition function provided`);
|
|
3808
|
+
}
|
|
3809
|
+
this._currentIteration = 0;
|
|
3810
|
+
let currentInput = { ...input };
|
|
3811
|
+
let currentOutput = {};
|
|
3812
|
+
while (this._currentIteration < this.maxIterations) {
|
|
3813
|
+
if (context.signal?.aborted) {
|
|
3814
|
+
break;
|
|
3815
|
+
}
|
|
3816
|
+
this.subGraph = this.cloneTemplateGraph(this._currentIteration);
|
|
3817
|
+
const results = await this.subGraph.run(currentInput, {
|
|
3818
|
+
parentSignal: context.signal
|
|
3819
|
+
});
|
|
3820
|
+
currentOutput = this.subGraph.mergeExecuteOutputsToRunOutput(results, this.compoundMerge);
|
|
3821
|
+
if (!this.condition(currentOutput, this._currentIteration)) {
|
|
3822
|
+
break;
|
|
3823
|
+
}
|
|
3824
|
+
if (this.chainIterations) {
|
|
3825
|
+
currentInput = { ...currentInput, ...currentOutput };
|
|
3826
|
+
}
|
|
3827
|
+
this._currentIteration++;
|
|
3828
|
+
const progress = Math.min(this._currentIteration / this.maxIterations * 100, 99);
|
|
3829
|
+
await context.updateProgress(progress, `Iteration ${this._currentIteration}`);
|
|
3830
|
+
}
|
|
3831
|
+
return currentOutput;
|
|
3832
|
+
}
|
|
3833
|
+
cloneTemplateGraph(iteration) {
|
|
3834
|
+
const clonedGraph = new TaskGraph;
|
|
3835
|
+
if (!this._templateGraph) {
|
|
3836
|
+
return clonedGraph;
|
|
3837
|
+
}
|
|
3838
|
+
const templateTasks = this._templateGraph.getTasks();
|
|
3839
|
+
const templateDataflows = this._templateGraph.getDataflows();
|
|
3840
|
+
const idMap = new Map;
|
|
3841
|
+
for (const templateTask of templateTasks) {
|
|
3842
|
+
const TaskClass = templateTask.constructor;
|
|
3843
|
+
const clonedTask = new TaskClass({ ...templateTask.defaults }, {
|
|
3844
|
+
...templateTask.config,
|
|
3845
|
+
id: `${templateTask.config.id}_iter${iteration}`,
|
|
3846
|
+
name: `${templateTask.config.name || templateTask.type} [${iteration}]`
|
|
3847
|
+
});
|
|
3848
|
+
clonedGraph.addTask(clonedTask);
|
|
3849
|
+
idMap.set(templateTask.config.id, clonedTask.config.id);
|
|
3850
|
+
}
|
|
3851
|
+
for (const templateDataflow of templateDataflows) {
|
|
3852
|
+
const sourceId = idMap.get(templateDataflow.sourceTaskId);
|
|
3853
|
+
const targetId = idMap.get(templateDataflow.targetTaskId);
|
|
3854
|
+
if (sourceId !== undefined && targetId !== undefined) {
|
|
3855
|
+
const { Dataflow: Dataflow2 } = (init_Dataflow(), __toCommonJS(exports_Dataflow));
|
|
3856
|
+
const clonedDataflow = new Dataflow2(sourceId, templateDataflow.sourceTaskPortId, targetId, templateDataflow.targetTaskPortId);
|
|
3857
|
+
clonedGraph.addDataflow(clonedDataflow);
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
return clonedGraph;
|
|
3861
|
+
}
|
|
3862
|
+
static inputSchema() {
|
|
3863
|
+
return {
|
|
3864
|
+
type: "object",
|
|
3865
|
+
properties: {},
|
|
3866
|
+
additionalProperties: true
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3869
|
+
static outputSchema() {
|
|
3870
|
+
return {
|
|
3871
|
+
type: "object",
|
|
3872
|
+
properties: {
|
|
3873
|
+
_iterations: {
|
|
3874
|
+
type: "number",
|
|
3875
|
+
title: "Iterations",
|
|
3876
|
+
description: "Number of iterations executed"
|
|
3877
|
+
}
|
|
3878
|
+
},
|
|
3879
|
+
additionalProperties: true
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
outputSchema() {
|
|
3883
|
+
if (!this._templateGraph) {
|
|
3884
|
+
return this.constructor.outputSchema();
|
|
3885
|
+
}
|
|
3886
|
+
const tasks = this._templateGraph.getTasks();
|
|
3887
|
+
const endingNodes = tasks.filter((task) => this._templateGraph.getTargetDataflows(task.config.id).length === 0);
|
|
3888
|
+
if (endingNodes.length === 0) {
|
|
3889
|
+
return this.constructor.outputSchema();
|
|
3890
|
+
}
|
|
3891
|
+
const properties = {
|
|
3892
|
+
_iterations: {
|
|
3893
|
+
type: "number",
|
|
3894
|
+
title: "Iterations",
|
|
3895
|
+
description: "Number of iterations executed"
|
|
3896
|
+
}
|
|
3897
|
+
};
|
|
3898
|
+
for (const task of endingNodes) {
|
|
3899
|
+
const taskOutputSchema = task.outputSchema();
|
|
3900
|
+
if (typeof taskOutputSchema === "boolean")
|
|
3901
|
+
continue;
|
|
3902
|
+
const taskProperties = taskOutputSchema.properties || {};
|
|
3903
|
+
for (const [key, schema] of Object.entries(taskProperties)) {
|
|
3904
|
+
if (!properties[key]) {
|
|
3905
|
+
properties[key] = schema;
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
return {
|
|
3910
|
+
type: "object",
|
|
3911
|
+
properties,
|
|
3912
|
+
additionalProperties: false
|
|
3913
|
+
};
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
Workflow.prototype.while = CreateLoopWorkflow(WhileTask);
|
|
3917
|
+
Workflow.prototype.endWhile = CreateEndLoopWorkflow("endWhile");
|
|
3918
|
+
// src/task/index.ts
|
|
3919
|
+
var registerBaseTasks = () => {
|
|
3920
|
+
const tasks = [
|
|
3921
|
+
ConditionalTask,
|
|
3922
|
+
GraphAsTask,
|
|
3923
|
+
ForEachTask,
|
|
3924
|
+
MapTask,
|
|
3925
|
+
BatchTask,
|
|
3926
|
+
WhileTask,
|
|
3927
|
+
ReduceTask
|
|
3928
|
+
];
|
|
3929
|
+
tasks.map(TaskRegistry.registerTask);
|
|
3930
|
+
return tasks;
|
|
3931
|
+
};
|
|
2844
3932
|
// src/storage/TaskGraphRepository.ts
|
|
2845
3933
|
import { createServiceToken as createServiceToken3, EventEmitter as EventEmitter6 } from "@workglow/util";
|
|
2846
3934
|
var TASK_GRAPH_REPOSITORY = createServiceToken3("taskgraph.taskGraphRepository");
|
|
@@ -3000,7 +4088,9 @@ class TaskOutputTabularRepository extends TaskOutputRepository {
|
|
|
3000
4088
|
export {
|
|
3001
4089
|
setTaskQueueRegistry,
|
|
3002
4090
|
serialGraph,
|
|
4091
|
+
resolveSchemaInputs,
|
|
3003
4092
|
registerJobQueueFactory,
|
|
4093
|
+
registerBaseTasks,
|
|
3004
4094
|
pipe,
|
|
3005
4095
|
parallel,
|
|
3006
4096
|
getTaskQueueRegistry,
|
|
@@ -3015,6 +4105,7 @@ export {
|
|
|
3015
4105
|
connect,
|
|
3016
4106
|
WorkflowError,
|
|
3017
4107
|
Workflow,
|
|
4108
|
+
WhileTask,
|
|
3018
4109
|
TaskStatus,
|
|
3019
4110
|
TaskRegistry,
|
|
3020
4111
|
TaskQueueRegistry,
|
|
@@ -3037,15 +4128,18 @@ export {
|
|
|
3037
4128
|
Task,
|
|
3038
4129
|
TASK_OUTPUT_REPOSITORY,
|
|
3039
4130
|
TASK_GRAPH_REPOSITORY,
|
|
4131
|
+
ReduceTask,
|
|
3040
4132
|
PROPERTY_ARRAY,
|
|
3041
|
-
|
|
4133
|
+
MapTask,
|
|
3042
4134
|
JobTaskFailedError,
|
|
3043
4135
|
JobQueueTask,
|
|
3044
4136
|
JOB_QUEUE_FACTORY,
|
|
3045
|
-
|
|
4137
|
+
IteratorTaskRunner,
|
|
4138
|
+
IteratorTask,
|
|
3046
4139
|
GraphAsTaskRunner,
|
|
3047
4140
|
GraphAsTask,
|
|
3048
4141
|
GRAPH_RESULT_ARRAY,
|
|
4142
|
+
ForEachTask,
|
|
3049
4143
|
EventTaskGraphToDagMapping,
|
|
3050
4144
|
EventDagToTaskGraphMapping,
|
|
3051
4145
|
DataflowArrow,
|
|
@@ -3053,8 +4147,10 @@ export {
|
|
|
3053
4147
|
DATAFLOW_ERROR_PORT,
|
|
3054
4148
|
DATAFLOW_ALL_PORTS,
|
|
3055
4149
|
CreateWorkflow,
|
|
4150
|
+
CreateLoopWorkflow,
|
|
4151
|
+
CreateEndLoopWorkflow,
|
|
3056
4152
|
ConditionalTask,
|
|
3057
|
-
|
|
4153
|
+
BatchTask
|
|
3058
4154
|
};
|
|
3059
4155
|
|
|
3060
|
-
//# debugId=
|
|
4156
|
+
//# debugId=BB6441A3D3B3FDA564756E2164756E21
|