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