@workglow/task-graph 0.0.85 → 0.0.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +1 -53
  2. package/dist/browser.js +281 -273
  3. package/dist/browser.js.map +19 -20
  4. package/dist/bun.js +281 -273
  5. package/dist/bun.js.map +19 -20
  6. package/dist/common.d.ts +1 -16
  7. package/dist/common.d.ts.map +1 -1
  8. package/dist/node.js +281 -273
  9. package/dist/node.js.map +19 -20
  10. package/dist/storage/TaskGraphTabularRepository.d.ts +2 -2
  11. package/dist/storage/TaskGraphTabularRepository.d.ts.map +1 -1
  12. package/dist/storage/TaskOutputTabularRepository.d.ts +2 -2
  13. package/dist/storage/TaskOutputTabularRepository.d.ts.map +1 -1
  14. package/dist/task/ConditionalTask.d.ts +1 -0
  15. package/dist/task/ConditionalTask.d.ts.map +1 -1
  16. package/dist/task/GraphAsTask.d.ts +2 -0
  17. package/dist/task/GraphAsTask.d.ts.map +1 -1
  18. package/dist/task/GraphAsTaskRunner.d.ts +0 -1
  19. package/dist/task/GraphAsTaskRunner.d.ts.map +1 -1
  20. package/dist/task/ITask.d.ts +5 -6
  21. package/dist/task/ITask.d.ts.map +1 -1
  22. package/dist/task/InputResolver.d.ts +34 -0
  23. package/dist/task/InputResolver.d.ts.map +1 -0
  24. package/dist/task/JobQueueTask.d.ts +3 -3
  25. package/dist/task/JobQueueTask.d.ts.map +1 -1
  26. package/dist/task/Task.d.ts +22 -11
  27. package/dist/task/Task.d.ts.map +1 -1
  28. package/dist/task/TaskEvents.d.ts +1 -1
  29. package/dist/task/TaskEvents.d.ts.map +1 -1
  30. package/dist/task/TaskJSON.d.ts +1 -4
  31. package/dist/task/TaskJSON.d.ts.map +1 -1
  32. package/dist/task/TaskRunner.d.ts +6 -5
  33. package/dist/task/TaskRunner.d.ts.map +1 -1
  34. package/dist/task/TaskTypes.d.ts +0 -4
  35. package/dist/task/TaskTypes.d.ts.map +1 -1
  36. package/dist/task/index.d.ts +23 -0
  37. package/dist/task/index.d.ts.map +1 -0
  38. package/dist/task-graph/Dataflow.d.ts +2 -3
  39. package/dist/task-graph/Dataflow.d.ts.map +1 -1
  40. package/dist/task-graph/ITaskGraph.d.ts +1 -1
  41. package/dist/task-graph/ITaskGraph.d.ts.map +1 -1
  42. package/dist/task-graph/TaskGraph.d.ts +4 -4
  43. package/dist/task-graph/TaskGraph.d.ts.map +1 -1
  44. package/dist/task-graph/TaskGraphRunner.d.ts +11 -18
  45. package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
  46. package/dist/task-graph/Workflow.d.ts +1 -1
  47. package/dist/task-graph/Workflow.d.ts.map +1 -1
  48. package/package.json +7 -7
  49. package/src/storage/README.md +1 -1
  50. package/src/task/README.md +91 -15
  51. package/src/task-graph/README.md +0 -2
  52. package/dist/task/ArrayTask.d.ts +0 -77
  53. package/dist/task/ArrayTask.d.ts.map +0 -1
  54. package/dist/task/InputTask.d.ts +0 -27
  55. package/dist/task/InputTask.d.ts.map +0 -1
  56. package/dist/task/OutputTask.d.ts +0 -27
  57. package/dist/task/OutputTask.d.ts.map +0 -1
package/dist/bun.js CHANGED
@@ -1,13 +1,4 @@
1
1
  // @bun
2
- // src/task/ArrayTask.ts
3
- import { uuid4 as uuid44 } from "@workglow/util";
4
-
5
- // src/task-graph/TaskGraph.ts
6
- import { DirectedAcyclicGraph, EventEmitter as EventEmitter5, uuid4 as uuid43 } from "@workglow/util";
7
-
8
- // src/task/GraphAsTask.ts
9
- import { compileSchema as compileSchema2 } from "@workglow/util";
10
-
11
2
  // src/task-graph/Dataflow.ts
12
3
  import { areSemanticallyCompatible, EventEmitter } from "@workglow/util";
13
4
 
@@ -43,14 +34,12 @@ class Dataflow {
43
34
  return Dataflow.createId(this.sourceTaskId, this.sourceTaskPortId, this.targetTaskId, this.targetTaskPortId);
44
35
  }
45
36
  value = undefined;
46
- provenance = {};
47
37
  status = TaskStatus.PENDING;
48
38
  error;
49
39
  reset() {
50
40
  this.status = TaskStatus.PENDING;
51
41
  this.error = undefined;
52
42
  this.value = undefined;
53
- this.provenance = {};
54
43
  this.emit("reset");
55
44
  this.emit("status", this.status);
56
45
  }
@@ -80,7 +69,7 @@ class Dataflow {
80
69
  }
81
70
  this.emit("status", this.status);
82
71
  }
83
- setPortData(entireDataBlock, nodeProvenance) {
72
+ setPortData(entireDataBlock) {
84
73
  if (this.sourceTaskPortId === DATAFLOW_ALL_PORTS) {
85
74
  this.value = entireDataBlock;
86
75
  } else if (this.sourceTaskPortId === DATAFLOW_ERROR_PORT) {
@@ -88,8 +77,6 @@ class Dataflow {
88
77
  } else {
89
78
  this.value = entireDataBlock[this.sourceTaskPortId];
90
79
  }
91
- if (nodeProvenance)
92
- this.provenance = nodeProvenance;
93
80
  }
94
81
  getPortData() {
95
82
  if (this.targetTaskPortId === DATAFLOW_ALL_PORTS) {
@@ -172,11 +159,17 @@ class DataflowArrow extends Dataflow {
172
159
  super(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
173
160
  }
174
161
  }
162
+ // src/task-graph/TaskGraph.ts
163
+ import { DirectedAcyclicGraph, EventEmitter as EventEmitter5, uuid4 as uuid43 } from "@workglow/util";
164
+
165
+ // src/task/GraphAsTask.ts
166
+ import { compileSchema as compileSchema2 } from "@workglow/util";
175
167
 
176
168
  // src/task-graph/TaskGraphRunner.ts
177
169
  import {
178
170
  collectPropertyValues,
179
171
  globalServiceRegistry as globalServiceRegistry2,
172
+ ServiceRegistry as ServiceRegistry2,
180
173
  uuid4 as uuid42
181
174
  } from "@workglow/util";
182
175
 
@@ -281,13 +274,69 @@ class TaskInvalidInputError extends TaskError {
281
274
 
282
275
  // src/task/TaskRunner.ts
283
276
  import { globalServiceRegistry } from "@workglow/util";
277
+
278
+ // src/task/InputResolver.ts
279
+ import { getInputResolvers } from "@workglow/util";
280
+ function getSchemaFormat(schema) {
281
+ if (typeof schema !== "object" || schema === null)
282
+ return;
283
+ const s = schema;
284
+ if (typeof s.format === "string")
285
+ return s.format;
286
+ const variants = s.oneOf ?? s.anyOf;
287
+ if (Array.isArray(variants)) {
288
+ for (const variant of variants) {
289
+ if (typeof variant === "object" && variant !== null) {
290
+ const v = variant;
291
+ if (typeof v.format === "string")
292
+ return v.format;
293
+ }
294
+ }
295
+ }
296
+ return;
297
+ }
298
+ function getFormatPrefix(format) {
299
+ const colonIndex = format.indexOf(":");
300
+ return colonIndex >= 0 ? format.substring(0, colonIndex) : format;
301
+ }
302
+ async function resolveSchemaInputs(input, schema, config) {
303
+ if (typeof schema === "boolean")
304
+ return input;
305
+ const properties = schema.properties;
306
+ if (!properties || typeof properties !== "object")
307
+ return input;
308
+ const resolvers = getInputResolvers();
309
+ const resolved = { ...input };
310
+ for (const [key, propSchema] of Object.entries(properties)) {
311
+ const value = resolved[key];
312
+ const format = getSchemaFormat(propSchema);
313
+ if (!format)
314
+ continue;
315
+ let resolver = resolvers.get(format);
316
+ if (!resolver) {
317
+ const prefix = getFormatPrefix(format);
318
+ resolver = resolvers.get(prefix);
319
+ }
320
+ if (!resolver)
321
+ continue;
322
+ if (typeof value === "string") {
323
+ resolved[key] = await resolver(value, format, config.registry);
324
+ } else if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
325
+ const results = await Promise.all(value.map((item) => resolver(item, format, config.registry)));
326
+ resolved[key] = results.filter((result) => result !== undefined);
327
+ }
328
+ }
329
+ return resolved;
330
+ }
331
+
332
+ // src/task/TaskRunner.ts
284
333
  class TaskRunner {
285
334
  running = false;
286
335
  reactiveRunning = false;
287
- nodeProvenance = {};
288
336
  task;
289
337
  abortController;
290
338
  outputCache;
339
+ registry = globalServiceRegistry;
291
340
  constructor(task) {
292
341
  this.task = task;
293
342
  this.own = this.own.bind(this);
@@ -297,6 +346,8 @@ class TaskRunner {
297
346
  await this.handleStart(config);
298
347
  try {
299
348
  this.task.setInput(overrides);
349
+ const schema = this.task.constructor.inputSchema();
350
+ this.task.runInputData = await resolveSchemaInputs(this.task.runInputData, schema, { registry: this.registry });
300
351
  const isValid = await this.task.validateInput(this.task.runInputData);
301
352
  if (!isValid) {
302
353
  throw new TaskInvalidInputError("Invalid input data");
@@ -332,6 +383,8 @@ class TaskRunner {
332
383
  return this.task.runOutputData;
333
384
  }
334
385
  this.task.setInput(overrides);
386
+ const schema = this.task.constructor.inputSchema();
387
+ this.task.runInputData = await resolveSchemaInputs(this.task.runInputData, schema, { registry: this.registry });
335
388
  await this.handleStartReactive();
336
389
  try {
337
390
  const isValid = await this.task.validateInput(this.task.runInputData);
@@ -362,8 +415,8 @@ class TaskRunner {
362
415
  const result = await this.task.execute(input, {
363
416
  signal: this.abortController.signal,
364
417
  updateProgress: this.handleProgress.bind(this),
365
- nodeProvenance: this.nodeProvenance,
366
- own: this.own
418
+ own: this.own,
419
+ registry: this.registry
367
420
  });
368
421
  return await this.executeTaskReactive(input, result || {});
369
422
  }
@@ -374,7 +427,6 @@ class TaskRunner {
374
427
  async handleStart(config = {}) {
375
428
  if (this.task.status === TaskStatus.PROCESSING)
376
429
  return;
377
- this.nodeProvenance = {};
378
430
  this.running = true;
379
431
  this.task.startedAt = new Date;
380
432
  this.task.progress = 0;
@@ -383,7 +435,6 @@ class TaskRunner {
383
435
  this.abortController.signal.addEventListener("abort", () => {
384
436
  this.handleAbort();
385
437
  });
386
- this.nodeProvenance = config.nodeProvenance ?? {};
387
438
  const cache = this.task.config.outputCache ?? config.outputCache;
388
439
  if (cache === true) {
389
440
  let instance = globalServiceRegistry.get(TASK_OUTPUT_REPOSITORY);
@@ -396,6 +447,9 @@ class TaskRunner {
396
447
  if (config.updateProgress) {
397
448
  this.updateProgress = config.updateProgress;
398
449
  }
450
+ if (config.registry) {
451
+ this.registry = config.registry;
452
+ }
399
453
  this.task.emit("start");
400
454
  this.task.emit("status", this.task.status);
401
455
  }
@@ -422,7 +476,6 @@ class TaskRunner {
422
476
  this.task.progress = 100;
423
477
  this.task.status = TaskStatus.COMPLETED;
424
478
  this.abortController = undefined;
425
- this.nodeProvenance = {};
426
479
  this.task.emit("complete");
427
480
  this.task.emit("status", this.task.status);
428
481
  }
@@ -436,7 +489,6 @@ class TaskRunner {
436
489
  this.task.progress = 100;
437
490
  this.task.completedAt = new Date;
438
491
  this.abortController = undefined;
439
- this.nodeProvenance = {};
440
492
  this.task.emit("disabled");
441
493
  this.task.emit("status", this.task.status);
442
494
  }
@@ -456,7 +508,6 @@ class TaskRunner {
456
508
  this.task.status = TaskStatus.FAILED;
457
509
  this.task.error = err instanceof TaskError ? err : new TaskFailedError(err?.message || "Task failed");
458
510
  this.abortController = undefined;
459
- this.nodeProvenance = {};
460
511
  this.task.emit("error", this.task.error);
461
512
  this.task.emit("status", this.task.status);
462
513
  }
@@ -554,7 +605,6 @@ class Task {
554
605
  return this._events;
555
606
  }
556
607
  _events;
557
- nodeProvenance = {};
558
608
  constructor(callerDefaultInputs = {}, config = {}) {
559
609
  const inputDefaults = this.getDefaultInputsFromStaticInputDefinitions();
560
610
  const mergedDefaults = Object.assign(inputDefaults, callerDefaultInputs);
@@ -591,10 +641,45 @@ class Task {
591
641
  }
592
642
  }
593
643
  resetInputData() {
644
+ this.runInputData = this.smartClone(this.defaults);
645
+ }
646
+ smartClone(obj, visited = new WeakSet) {
647
+ if (obj === null || obj === undefined) {
648
+ return obj;
649
+ }
650
+ if (typeof obj !== "object") {
651
+ return obj;
652
+ }
653
+ if (visited.has(obj)) {
654
+ throw new Error("Circular reference detected in input data. " + "Cannot clone objects with circular references.");
655
+ }
656
+ if (ArrayBuffer.isView(obj)) {
657
+ if (typeof DataView !== "undefined" && obj instanceof DataView) {
658
+ return obj;
659
+ }
660
+ const typedArray = obj;
661
+ return new typedArray.constructor(typedArray);
662
+ }
663
+ if (!Array.isArray(obj)) {
664
+ const proto = Object.getPrototypeOf(obj);
665
+ if (proto !== Object.prototype && proto !== null) {
666
+ return obj;
667
+ }
668
+ }
669
+ visited.add(obj);
594
670
  try {
595
- this.runInputData = structuredClone(this.defaults);
596
- } catch (err) {
597
- this.runInputData = JSON.parse(JSON.stringify(this.defaults));
671
+ if (Array.isArray(obj)) {
672
+ return obj.map((item) => this.smartClone(item, visited));
673
+ }
674
+ const result = {};
675
+ for (const key in obj) {
676
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
677
+ result[key] = this.smartClone(obj[key], visited);
678
+ }
679
+ }
680
+ return result;
681
+ } finally {
682
+ visited.delete(obj);
598
683
  }
599
684
  }
600
685
  setDefaults(defaults) {
@@ -622,7 +707,7 @@ class Task {
622
707
  }
623
708
  if (schema.additionalProperties === true) {
624
709
  for (const [inputId, value] of Object.entries(input)) {
625
- if (value !== undefined && !(inputId in properties)) {
710
+ if (!(inputId in properties)) {
626
711
  this.runInputData[inputId] = value;
627
712
  }
628
713
  }
@@ -675,7 +760,7 @@ class Task {
675
760
  }
676
761
  if (inputSchema.additionalProperties === true) {
677
762
  for (const [inputId, value] of Object.entries(overrides)) {
678
- if (value !== undefined && !(inputId in properties)) {
763
+ if (!(inputId in properties)) {
679
764
  if (!deepEqual(this.runInputData[inputId], value)) {
680
765
  this.runInputData[inputId] = value;
681
766
  changed = true;
@@ -685,7 +770,7 @@ class Task {
685
770
  }
686
771
  return changed;
687
772
  }
688
- async narrowInput(input) {
773
+ async narrowInput(input, _registry) {
689
774
  return input;
690
775
  }
691
776
  subscribe(name, fn) {
@@ -745,20 +830,20 @@ class Task {
745
830
  const path = e.data.pointer || "";
746
831
  return `${e.message}${path ? ` (${path})` : ""}`;
747
832
  });
748
- throw new TaskInvalidInputError(`Input ${JSON.stringify(input)} does not match schema: ${errorMessages.join(", ")}`);
833
+ throw new TaskInvalidInputError(`Input ${JSON.stringify(Object.keys(input))} does not match schema: ${errorMessages.join(", ")}`);
749
834
  }
750
835
  return true;
751
836
  }
752
837
  id() {
753
838
  return this.config.id;
754
839
  }
755
- getProvenance() {
756
- return this.config.provenance ?? {};
757
- }
758
840
  stripSymbols(obj) {
759
841
  if (obj === null || obj === undefined) {
760
842
  return obj;
761
843
  }
844
+ if (ArrayBuffer.isView(obj)) {
845
+ return obj;
846
+ }
762
847
  if (Array.isArray(obj)) {
763
848
  return obj.map((item) => this.stripSymbols(item));
764
849
  }
@@ -774,14 +859,12 @@ class Task {
774
859
  return obj;
775
860
  }
776
861
  toJSON() {
777
- const provenance = this.getProvenance();
778
862
  const extras = this.config.extras;
779
863
  let json = this.stripSymbols({
780
864
  id: this.config.id,
781
865
  type: this.type,
782
866
  ...this.config.name ? { name: this.config.name } : {},
783
867
  defaults: this.defaults,
784
- ...Object.keys(provenance).length ? { provenance } : {},
785
868
  ...extras && Object.keys(extras).length ? { extras } : {}
786
869
  });
787
870
  return json;
@@ -827,8 +910,9 @@ class Task {
827
910
  // src/task/ConditionalTask.ts
828
911
  class ConditionalTask extends Task {
829
912
  static type = "ConditionalTask";
830
- static category = "Flow Control";
913
+ static category = "Hidden";
831
914
  static title = "Conditional Task";
915
+ static description = "Evaluates conditions to determine which dataflows are active";
832
916
  static hasDynamicSchemas = true;
833
917
  activeBranches = new Set;
834
918
  async execute(input, context) {
@@ -1053,9 +1137,9 @@ class TaskGraphRunner {
1053
1137
  reactiveScheduler;
1054
1138
  running = false;
1055
1139
  reactiveRunning = false;
1056
- provenanceInput;
1057
1140
  graph;
1058
1141
  outputCache;
1142
+ registry = globalServiceRegistry2;
1059
1143
  abortController;
1060
1144
  inProgressTasks = new Map;
1061
1145
  inProgressFunctions = new Map;
@@ -1064,7 +1148,6 @@ class TaskGraphRunner {
1064
1148
  this.processScheduler = processScheduler;
1065
1149
  this.reactiveScheduler = reactiveScheduler;
1066
1150
  this.graph = graph;
1067
- this.provenanceInput = new Map;
1068
1151
  graph.outputCache = outputCache;
1069
1152
  this.handleProgress = this.handleProgress.bind(this);
1070
1153
  }
@@ -1084,7 +1167,7 @@ class TaskGraphRunner {
1084
1167
  const runAsync = async () => {
1085
1168
  try {
1086
1169
  const taskInput = isRootTask ? input : this.filterInputForTask(task, input);
1087
- const taskPromise = this.runTaskWithProvenance(task, taskInput, config?.parentProvenance || {});
1170
+ const taskPromise = this.runTask(task, taskInput);
1088
1171
  this.inProgressTasks.set(task.config.id, taskPromise);
1089
1172
  const taskResult = await taskPromise;
1090
1173
  if (this.graph.getTargetDataflows(task.config.id).length === 0) {
@@ -1194,23 +1277,16 @@ class TaskGraphRunner {
1194
1277
  this.addInputData(task, dataflow.getPortData());
1195
1278
  }
1196
1279
  }
1197
- getInputProvenance(node) {
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) {
1280
+ async pushOutputFromNodeToEdges(node, results) {
1205
1281
  const dataflows = this.graph.getTargetDataflows(node.config.id);
1206
1282
  for (const dataflow of dataflows) {
1207
1283
  const compatibility = dataflow.semanticallyCompatible(this.graph, dataflow);
1208
1284
  if (compatibility === "static") {
1209
- dataflow.setPortData(results, nodeProvenance);
1285
+ dataflow.setPortData(results);
1210
1286
  } else if (compatibility === "runtime") {
1211
1287
  const task = this.graph.getTask(dataflow.targetTaskId);
1212
- const narrowed = await task.narrowInput({ ...results });
1213
- dataflow.setPortData(narrowed, nodeProvenance);
1288
+ const narrowed = await task.narrowInput({ ...results }, this.registry);
1289
+ dataflow.setPortData(narrowed);
1214
1290
  } else {}
1215
1291
  }
1216
1292
  }
@@ -1280,20 +1356,14 @@ class TaskGraphRunner {
1280
1356
  }
1281
1357
  }
1282
1358
  }
1283
- async runTaskWithProvenance(task, input, parentProvenance) {
1284
- const nodeProvenance = {
1285
- ...parentProvenance,
1286
- ...this.getInputProvenance(task),
1287
- ...task.getProvenance()
1288
- };
1289
- this.provenanceInput.set(task.config.id, nodeProvenance);
1359
+ async runTask(task, input) {
1290
1360
  this.copyInputFromEdgesToNode(task);
1291
1361
  const results = await task.runner.run(input, {
1292
- nodeProvenance,
1293
1362
  outputCache: this.outputCache,
1294
- updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args)
1363
+ updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
1364
+ registry: this.registry
1295
1365
  });
1296
- await this.pushOutputFromNodeToEdges(task, results, nodeProvenance);
1366
+ await this.pushOutputFromNodeToEdges(task, results);
1297
1367
  return {
1298
1368
  id: task.config.id,
1299
1369
  type: task.constructor.runtype || task.constructor.type,
@@ -1327,10 +1397,15 @@ class TaskGraphRunner {
1327
1397
  });
1328
1398
  }
1329
1399
  async handleStart(config) {
1400
+ if (config?.registry !== undefined) {
1401
+ this.registry = config.registry;
1402
+ } else {
1403
+ this.registry = new ServiceRegistry2(globalServiceRegistry2.container.createChildContainer());
1404
+ }
1330
1405
  if (config?.outputCache !== undefined) {
1331
1406
  if (typeof config.outputCache === "boolean") {
1332
1407
  if (config.outputCache === true) {
1333
- this.outputCache = globalServiceRegistry2.get(TASK_OUTPUT_REPOSITORY);
1408
+ this.outputCache = this.registry.get(TASK_OUTPUT_REPOSITORY);
1334
1409
  } else {
1335
1410
  this.outputCache = undefined;
1336
1411
  }
@@ -1416,7 +1491,7 @@ class TaskGraphRunner {
1416
1491
  progress = Math.round(completed / total);
1417
1492
  }
1418
1493
  this.pushStatusFromNodeToEdges(this.graph, task);
1419
- await this.pushOutputFromNodeToEdges(task, task.runOutputData, task.getProvenance());
1494
+ await this.pushOutputFromNodeToEdges(task, task.runOutputData);
1420
1495
  this.graph.emit("graph_progress", progress, message, args);
1421
1496
  }
1422
1497
  }
@@ -1428,7 +1503,6 @@ class GraphAsTaskRunner extends TaskRunner {
1428
1503
  this.task.emit("progress", progress, message, ...args);
1429
1504
  });
1430
1505
  const results = await this.task.subGraph.run(input, {
1431
- parentProvenance: this.nodeProvenance || {},
1432
1506
  parentSignal: this.abortController?.signal,
1433
1507
  outputCache: this.outputCache
1434
1508
  });
@@ -1444,27 +1518,12 @@ class GraphAsTaskRunner extends TaskRunner {
1444
1518
  }
1445
1519
  super.handleDisable();
1446
1520
  }
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
1521
  async executeTask(input) {
1463
1522
  if (this.task.hasChildren()) {
1464
1523
  const runExecuteOutputData = await this.executeTaskChildren(input);
1465
1524
  this.task.runOutputData = this.task.subGraph.mergeExecuteOutputsToRunOutput(runExecuteOutputData, this.task.compoundMerge);
1466
1525
  } else {
1467
- const result = await super.executeTask(this.fixInput(input));
1526
+ const result = await super.executeTask(input);
1468
1527
  this.task.runOutputData = result ?? {};
1469
1528
  }
1470
1529
  return this.task.runOutputData;
@@ -1474,7 +1533,7 @@ class GraphAsTaskRunner extends TaskRunner {
1474
1533
  const reactiveResults = await this.executeTaskChildrenReactive();
1475
1534
  this.task.runOutputData = this.task.subGraph.mergeExecuteOutputsToRunOutput(reactiveResults, this.task.compoundMerge);
1476
1535
  } else {
1477
- const reactiveResults = await super.executeTaskReactive(this.fixInput(input), output);
1536
+ const reactiveResults = await super.executeTaskReactive(input, output);
1478
1537
  this.task.runOutputData = Object.assign({}, output, reactiveResults ?? {});
1479
1538
  }
1480
1539
  return this.task.runOutputData;
@@ -1484,6 +1543,8 @@ class GraphAsTaskRunner extends TaskRunner {
1484
1543
  // src/task/GraphAsTask.ts
1485
1544
  class GraphAsTask extends Task {
1486
1545
  static type = "GraphAsTask";
1546
+ static title = "Graph as Task";
1547
+ static description = "A task that contains a subgraph of tasks";
1487
1548
  static category = "Hidden";
1488
1549
  static compoundMerge = PROPERTY_ARRAY;
1489
1550
  static hasDynamicSchemas = true;
@@ -1710,6 +1771,15 @@ class Workflow {
1710
1771
  const sourceSchema = parent.outputSchema();
1711
1772
  const targetSchema = task.inputSchema();
1712
1773
  const makeMatch = (comparator) => {
1774
+ if (typeof sourceSchema === "object") {
1775
+ if (targetSchema === true || typeof targetSchema === "object" && targetSchema.additionalProperties === true) {
1776
+ for (const fromOutputPortId of Object.keys(sourceSchema.properties || {})) {
1777
+ matches.set(fromOutputPortId, fromOutputPortId);
1778
+ this.connect(parent.config.id, fromOutputPortId, task.config.id, fromOutputPortId);
1779
+ }
1780
+ return matches;
1781
+ }
1782
+ }
1713
1783
  if (typeof sourceSchema === "boolean" || typeof targetSchema === "boolean") {
1714
1784
  return matches;
1715
1785
  }
@@ -1723,25 +1793,135 @@ class Workflow {
1723
1793
  }
1724
1794
  return matches;
1725
1795
  };
1726
- makeMatch(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
1796
+ const getSpecificTypeIdentifiers = (schema) => {
1797
+ const formats = new Set;
1798
+ const ids = new Set;
1799
+ if (typeof schema === "boolean") {
1800
+ return { formats, ids };
1801
+ }
1802
+ const extractFromSchema = (s) => {
1803
+ if (!s || typeof s !== "object" || Array.isArray(s))
1804
+ return;
1805
+ if (s.format)
1806
+ formats.add(s.format);
1807
+ if (s.$id)
1808
+ ids.add(s.$id);
1809
+ };
1810
+ extractFromSchema(schema);
1811
+ const checkUnion = (schemas) => {
1812
+ if (!schemas)
1813
+ return;
1814
+ for (const s of schemas) {
1815
+ if (typeof s === "boolean")
1816
+ continue;
1817
+ extractFromSchema(s);
1818
+ if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
1819
+ extractFromSchema(s.items);
1820
+ }
1821
+ }
1822
+ };
1823
+ checkUnion(schema.oneOf);
1824
+ checkUnion(schema.anyOf);
1825
+ if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
1826
+ extractFromSchema(schema.items);
1827
+ }
1828
+ return { formats, ids };
1829
+ };
1830
+ const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
1727
1831
  if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
1728
- if (fromPortOutputSchema === true && toPortInputSchema === true) {
1832
+ return fromPortOutputSchema === true && toPortInputSchema === true;
1833
+ }
1834
+ const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
1835
+ const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
1836
+ for (const format of outputIds.formats) {
1837
+ if (inputIds.formats.has(format)) {
1838
+ return true;
1839
+ }
1840
+ }
1841
+ for (const id of outputIds.ids) {
1842
+ if (inputIds.ids.has(id)) {
1729
1843
  return true;
1730
1844
  }
1845
+ }
1846
+ if (requireSpecificType) {
1731
1847
  return false;
1732
1848
  }
1733
- const idTypeMatch = fromPortOutputSchema.$id !== undefined && fromPortOutputSchema.$id === toPortInputSchema.$id;
1734
1849
  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));
1850
+ if (!idTypeBlank)
1851
+ return false;
1852
+ if (fromPortOutputSchema.type === toPortInputSchema.type)
1853
+ return true;
1854
+ const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
1855
+ if (typeof schema === "boolean")
1856
+ return schema;
1857
+ return schema.type === fromPortOutputSchema.type;
1858
+ }) ?? false;
1859
+ const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
1860
+ if (typeof schema === "boolean")
1861
+ return schema;
1862
+ return schema.type === fromPortOutputSchema.type;
1863
+ }) ?? false;
1864
+ return matchesOneOf || matchesAnyOf;
1865
+ };
1866
+ makeMatch(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
1736
1867
  const outputPortIdMatch = fromOutputPortId === toInputPortId;
1737
1868
  const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
1738
1869
  const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
1739
- return (idTypeMatch || typeMatch) && portIdsCompatible;
1870
+ return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
1740
1871
  });
1741
- if (matches.size === 0) {
1742
- this._error = `Could not find a match between the outputs of ${parent.type} and the inputs of ${task.type}. ` + `You now need to connect the outputs to the inputs via connect() manually before adding this task. Task not added.`;
1872
+ makeMatch(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
1873
+ return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
1874
+ });
1875
+ const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
1876
+ const providedInputKeys = new Set(Object.keys(input || {}));
1877
+ const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r));
1878
+ let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
1879
+ if (unmatchedRequired.length > 0) {
1880
+ const nodes = this._graph.getTasks();
1881
+ const parentIndex = nodes.findIndex((n) => n.config.id === parent.config.id);
1882
+ for (let i = parentIndex - 1;i >= 0 && unmatchedRequired.length > 0; i--) {
1883
+ const earlierTask = nodes[i];
1884
+ const earlierOutputSchema = earlierTask.outputSchema();
1885
+ const makeMatchFromEarlier = (comparator) => {
1886
+ if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
1887
+ return;
1888
+ }
1889
+ for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
1890
+ for (const requiredInputId of unmatchedRequired) {
1891
+ const toPortInputSchema = targetSchema.properties?.[requiredInputId];
1892
+ if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
1893
+ matches.set(requiredInputId, fromOutputPortId);
1894
+ this.connect(earlierTask.config.id, fromOutputPortId, task.config.id, requiredInputId);
1895
+ }
1896
+ }
1897
+ }
1898
+ };
1899
+ makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
1900
+ const outputPortIdMatch = fromOutputPortId === toInputPortId;
1901
+ const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
1902
+ const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
1903
+ return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
1904
+ });
1905
+ makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
1906
+ return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
1907
+ });
1908
+ unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
1909
+ }
1910
+ }
1911
+ const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
1912
+ if (stillUnmatchedRequired.length > 0) {
1913
+ this._error = `Could not find matches for required inputs [${stillUnmatchedRequired.join(", ")}] of ${task.type}. ` + `Attempted to match from ${parent.type} and earlier tasks. Task not added.`;
1743
1914
  console.error(this._error);
1744
1915
  this.graph.removeTask(task.config.id);
1916
+ } else if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
1917
+ const hasRequiredInputs = requiredInputs.size > 0;
1918
+ const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
1919
+ const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
1920
+ if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
1921
+ this._error = `Could not find a match between the outputs of ${parent.type} and the inputs of ${task.type}. ` + `You now need to connect the outputs to the inputs via connect() manually before adding this task. Task not added.`;
1922
+ console.error(this._error);
1923
+ this.graph.removeTask(task.config.id);
1924
+ }
1745
1925
  }
1746
1926
  }
1747
1927
  return this;
@@ -1786,7 +1966,6 @@ class Workflow {
1786
1966
  try {
1787
1967
  const output = await this.graph.run(input, {
1788
1968
  parentSignal: this._abortController.signal,
1789
- parentProvenance: {},
1790
1969
  outputCache: this._repository
1791
1970
  });
1792
1971
  const results = this.graph.mergeExecuteOutputsToRunOutput(output, PROPERTY_ARRAY);
@@ -1918,7 +2097,8 @@ class Workflow {
1918
2097
  if (targetSchema === false) {
1919
2098
  throw new WorkflowError(`Target task has schema 'false' and accepts no inputs`);
1920
2099
  }
1921
- } else if (!targetSchema.properties?.[targetTaskPortId]) {
2100
+ if (targetSchema === true) {}
2101
+ } else if (targetSchema.additionalProperties === true) {} else if (!targetSchema.properties?.[targetTaskPortId]) {
1922
2102
  throw new WorkflowError(`Input ${targetTaskPortId} not found on target task`);
1923
2103
  }
1924
2104
  const dataflow = new Dataflow(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
@@ -2098,7 +2278,6 @@ class TaskGraph {
2098
2278
  run(input = {}, config = {}) {
2099
2279
  return this.runner.runGraph(input, {
2100
2280
  outputCache: config?.outputCache || this.outputCache,
2101
- parentProvenance: config?.parentProvenance || {},
2102
2281
  parentSignal: config?.parentSignal || undefined
2103
2282
  });
2104
2283
  }
@@ -2339,142 +2518,6 @@ function serialGraph(tasks, inputHandle, outputHandle) {
2339
2518
  graph.addDataflows(serialGraphEdges(tasks, inputHandle, outputHandle));
2340
2519
  return graph;
2341
2520
  }
2342
-
2343
- // src/task/ArrayTask.ts
2344
- class ArrayTask extends GraphAsTask {
2345
- static type = "ArrayTask";
2346
- static compoundMerge = PROPERTY_ARRAY;
2347
- inputSchema() {
2348
- return this.constructor.inputSchema();
2349
- }
2350
- outputSchema() {
2351
- return this.constructor.outputSchema();
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;
2372
- }
2373
- const inputIds = Array.from(arrayInputs.keys());
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();
2383
- }
2384
- generateCombinations(input, inputMakeArray) {
2385
- const arraysToCombine = inputMakeArray.map((key) => Array.isArray(input[key]) ? input[key] : []);
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;
2410
- }
2411
- toJSON() {
2412
- const { subgraph, ...result } = super.toJSON();
2413
- return result;
2414
- }
2415
- toDependencyJSON() {
2416
- const { subtasks, ...result } = super.toDependencyJSON();
2417
- return result;
2418
- }
2419
- get runner() {
2420
- if (!this._runner) {
2421
- this._runner = new ArrayTaskRunner(this);
2422
- }
2423
- return this._runner;
2424
- }
2425
- }
2426
-
2427
- class ArrayTaskRunner extends GraphAsTaskRunner {
2428
- async executeTaskChildren(_input) {
2429
- return super.executeTaskChildren({});
2430
- }
2431
- async executeTaskReactive(input, output) {
2432
- const reactiveResults = await super.executeTaskReactive(input, output);
2433
- if (this.task.hasChildren()) {
2434
- return reactiveResults;
2435
- } else {
2436
- this.task.runOutputData = Object.assign({}, output, reactiveResults ?? {});
2437
- return this.task.runOutputData;
2438
- }
2439
- }
2440
- }
2441
- // src/task/InputTask.ts
2442
- import { Task as Task2 } from "@workglow/task-graph";
2443
-
2444
- class InputTask extends Task2 {
2445
- static type = "InputTask";
2446
- static category = "Flow Control";
2447
- static title = "Input";
2448
- static description = "Starts the workflow";
2449
- static hasDynamicSchemas = true;
2450
- static cacheable = false;
2451
- static inputSchema() {
2452
- return {
2453
- type: "object",
2454
- properties: {},
2455
- additionalProperties: true
2456
- };
2457
- }
2458
- static outputSchema() {
2459
- return {
2460
- type: "object",
2461
- properties: {},
2462
- additionalProperties: true
2463
- };
2464
- }
2465
- inputSchema() {
2466
- return this.config?.extras?.inputSchema ?? this.constructor.inputSchema();
2467
- }
2468
- outputSchema() {
2469
- return this.config?.extras?.outputSchema ?? this.constructor.outputSchema();
2470
- }
2471
- async execute(input, _context) {
2472
- return input;
2473
- }
2474
- async executeReactive(input, _output, _context) {
2475
- return input;
2476
- }
2477
- }
2478
2521
  // src/task/JobQueueFactory.ts
2479
2522
  import {
2480
2523
  JobQueueClient,
@@ -2603,7 +2646,7 @@ function setTaskQueueRegistry(registry) {
2603
2646
  }
2604
2647
 
2605
2648
  // src/task/JobQueueTask.ts
2606
- class JobQueueTask extends ArrayTask {
2649
+ class JobQueueTask extends GraphAsTask {
2607
2650
  static type = "JobQueueTask";
2608
2651
  static canRunDirectly = true;
2609
2652
  currentQueueName;
@@ -2735,43 +2778,6 @@ class JobQueueTask extends ArrayTask {
2735
2778
  super.abort();
2736
2779
  }
2737
2780
  }
2738
- // src/task/OutputTask.ts
2739
- import { Task as Task3 } from "@workglow/task-graph";
2740
-
2741
- class OutputTask extends Task3 {
2742
- static type = "OutputTask";
2743
- static category = "Flow Control";
2744
- static title = "Output";
2745
- static description = "Ends the workflow";
2746
- static hasDynamicSchemas = true;
2747
- static cacheable = false;
2748
- static inputSchema() {
2749
- return {
2750
- type: "object",
2751
- properties: {},
2752
- additionalProperties: true
2753
- };
2754
- }
2755
- static outputSchema() {
2756
- return {
2757
- type: "object",
2758
- properties: {},
2759
- additionalProperties: true
2760
- };
2761
- }
2762
- inputSchema() {
2763
- return this.config?.extras?.inputSchema ?? this.constructor.inputSchema();
2764
- }
2765
- outputSchema() {
2766
- return this.config?.extras?.outputSchema ?? this.constructor.outputSchema();
2767
- }
2768
- async execute(input, _context) {
2769
- return input;
2770
- }
2771
- async executeReactive(input, _output, _context) {
2772
- return input;
2773
- }
2774
- }
2775
2781
  // src/task/TaskRegistry.ts
2776
2782
  var taskConstructors = new Map;
2777
2783
  function registerTask(baseClass) {
@@ -2789,17 +2795,14 @@ var createSingleTaskFromJSON = (item) => {
2789
2795
  throw new TaskJSONError("Task id required");
2790
2796
  if (!item.type)
2791
2797
  throw new TaskJSONError("Task type required");
2792
- if (item.defaults && (Array.isArray(item.defaults) || Array.isArray(item.provenance)))
2798
+ if (item.defaults && Array.isArray(item.defaults))
2793
2799
  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
2800
  const taskClass = TaskRegistry.all.get(item.type);
2797
2801
  if (!taskClass)
2798
2802
  throw new TaskJSONError(`Task type ${item.type} not found, perhaps not registered?`);
2799
2803
  const taskConfig = {
2800
2804
  id: item.id,
2801
2805
  name: item.name,
2802
- provenance: item.provenance ?? {},
2803
2806
  extras: item.extras
2804
2807
  };
2805
2808
  const task = new taskClass(item.defaults ?? {}, taskConfig);
@@ -2842,6 +2845,12 @@ var createGraphFromGraphJSON = (graphJsonObj) => {
2842
2845
  }
2843
2846
  return subGraph;
2844
2847
  };
2848
+ // src/task/index.ts
2849
+ var registerBaseTasks = () => {
2850
+ const tasks = [ConditionalTask, GraphAsTask];
2851
+ tasks.map(TaskRegistry.registerTask);
2852
+ return tasks;
2853
+ };
2845
2854
  // src/storage/TaskGraphRepository.ts
2846
2855
  import { createServiceToken as createServiceToken3, EventEmitter as EventEmitter6 } from "@workglow/util";
2847
2856
  var TASK_GRAPH_REPOSITORY = createServiceToken3("taskgraph.taskGraphRepository");
@@ -3001,7 +3010,9 @@ class TaskOutputTabularRepository extends TaskOutputRepository {
3001
3010
  export {
3002
3011
  setTaskQueueRegistry,
3003
3012
  serialGraph,
3013
+ resolveSchemaInputs,
3004
3014
  registerJobQueueFactory,
3015
+ registerBaseTasks,
3005
3016
  pipe,
3006
3017
  parallel,
3007
3018
  getTaskQueueRegistry,
@@ -3039,11 +3050,9 @@ export {
3039
3050
  TASK_OUTPUT_REPOSITORY,
3040
3051
  TASK_GRAPH_REPOSITORY,
3041
3052
  PROPERTY_ARRAY,
3042
- OutputTask,
3043
3053
  JobTaskFailedError,
3044
3054
  JobQueueTask,
3045
3055
  JOB_QUEUE_FACTORY,
3046
- InputTask,
3047
3056
  GraphAsTaskRunner,
3048
3057
  GraphAsTask,
3049
3058
  GRAPH_RESULT_ARRAY,
@@ -3054,8 +3063,7 @@ export {
3054
3063
  DATAFLOW_ERROR_PORT,
3055
3064
  DATAFLOW_ALL_PORTS,
3056
3065
  CreateWorkflow,
3057
- ConditionalTask,
3058
- ArrayTask
3066
+ ConditionalTask
3059
3067
  };
3060
3068
 
3061
- //# debugId=B04A540E23B60ABE64756E2164756E21
3069
+ //# debugId=A58A1B322B71CA9464756E2164756E21