@workglow/task-graph 0.0.88 → 0.0.90

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 (43) hide show
  1. package/dist/browser.js +1789 -266
  2. package/dist/browser.js.map +19 -11
  3. package/dist/bun.js +1789 -266
  4. package/dist/bun.js.map +19 -11
  5. package/dist/node.js +1789 -266
  6. package/dist/node.js.map +19 -11
  7. package/dist/task/ConditionUtils.d.ts +47 -0
  8. package/dist/task/ConditionUtils.d.ts.map +1 -0
  9. package/dist/task/ConditionalTask.d.ts +15 -0
  10. package/dist/task/ConditionalTask.d.ts.map +1 -1
  11. package/dist/task/GraphAsTask.d.ts.map +1 -1
  12. package/dist/task/GraphAsTaskRunner.d.ts +3 -4
  13. package/dist/task/GraphAsTaskRunner.d.ts.map +1 -1
  14. package/dist/task/IteratorTask.d.ts +177 -0
  15. package/dist/task/IteratorTask.d.ts.map +1 -0
  16. package/dist/task/IteratorTaskRunner.d.ts +36 -0
  17. package/dist/task/IteratorTaskRunner.d.ts.map +1 -0
  18. package/dist/task/MapTask.d.ts +82 -0
  19. package/dist/task/MapTask.d.ts.map +1 -0
  20. package/dist/task/ReduceTask.d.ts +61 -0
  21. package/dist/task/ReduceTask.d.ts.map +1 -0
  22. package/dist/task/Task.d.ts +2 -2
  23. package/dist/task/Task.d.ts.map +1 -1
  24. package/dist/task/WhileTask.d.ts +214 -0
  25. package/dist/task/WhileTask.d.ts.map +1 -0
  26. package/dist/task/WhileTaskRunner.d.ts +29 -0
  27. package/dist/task/WhileTaskRunner.d.ts.map +1 -0
  28. package/dist/task/index.d.ts +12 -1
  29. package/dist/task/index.d.ts.map +1 -1
  30. package/dist/task/iterationSchema.d.ts +70 -0
  31. package/dist/task/iterationSchema.d.ts.map +1 -0
  32. package/dist/task-graph/ITaskGraph.d.ts +1 -1
  33. package/dist/task-graph/ITaskGraph.d.ts.map +1 -1
  34. package/dist/task-graph/TaskGraph.d.ts +1 -1
  35. package/dist/task-graph/TaskGraph.d.ts.map +1 -1
  36. package/dist/task-graph/TaskGraphRunner.d.ts +2 -1
  37. package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
  38. package/dist/task-graph/Workflow.d.ts +112 -8
  39. package/dist/task-graph/Workflow.d.ts.map +1 -1
  40. package/package.json +7 -7
  41. package/src/EXECUTION_MODEL.md +433 -0
  42. package/dist/task/TaskJSON.test.d.ts +0 -7
  43. package/dist/task/TaskJSON.test.d.ts.map +0 -1
package/dist/bun.js CHANGED
@@ -162,7 +162,7 @@ class DataflowArrow extends Dataflow {
162
162
  }
163
163
  }
164
164
  // src/task-graph/TaskGraph.ts
165
- import { DirectedAcyclicGraph, EventEmitter as EventEmitter5, uuid4 as uuid43 } from "@workglow/util";
165
+ import { DirectedAcyclicGraph, EventEmitter as EventEmitter5, uuid4 as uuid44 } from "@workglow/util";
166
166
 
167
167
  // src/task/GraphAsTask.ts
168
168
  import { compileSchema as compileSchema2 } from "@workglow/util";
@@ -205,6 +205,73 @@ class TaskOutputRepository {
205
205
  }
206
206
  }
207
207
 
208
+ // src/task/ConditionUtils.ts
209
+ function evaluateCondition(fieldValue, operator, compareValue) {
210
+ if (fieldValue === null || fieldValue === undefined) {
211
+ switch (operator) {
212
+ case "is_empty":
213
+ return true;
214
+ case "is_not_empty":
215
+ return false;
216
+ case "is_true":
217
+ return false;
218
+ case "is_false":
219
+ return true;
220
+ default:
221
+ return false;
222
+ }
223
+ }
224
+ const strValue = String(fieldValue);
225
+ const numValue = Number(fieldValue);
226
+ switch (operator) {
227
+ case "equals":
228
+ if (!isNaN(numValue) && !isNaN(Number(compareValue))) {
229
+ return numValue === Number(compareValue);
230
+ }
231
+ return strValue === compareValue;
232
+ case "not_equals":
233
+ if (!isNaN(numValue) && !isNaN(Number(compareValue))) {
234
+ return numValue !== Number(compareValue);
235
+ }
236
+ return strValue !== compareValue;
237
+ case "greater_than":
238
+ return numValue > Number(compareValue);
239
+ case "greater_or_equal":
240
+ return numValue >= Number(compareValue);
241
+ case "less_than":
242
+ return numValue < Number(compareValue);
243
+ case "less_or_equal":
244
+ return numValue <= Number(compareValue);
245
+ case "contains":
246
+ return strValue.toLowerCase().includes(compareValue.toLowerCase());
247
+ case "starts_with":
248
+ return strValue.toLowerCase().startsWith(compareValue.toLowerCase());
249
+ case "ends_with":
250
+ return strValue.toLowerCase().endsWith(compareValue.toLowerCase());
251
+ case "is_empty":
252
+ return strValue === "" || Array.isArray(fieldValue) && fieldValue.length === 0;
253
+ case "is_not_empty":
254
+ return strValue !== "" && !(Array.isArray(fieldValue) && fieldValue.length === 0);
255
+ case "is_true":
256
+ return Boolean(fieldValue) === true;
257
+ case "is_false":
258
+ return Boolean(fieldValue) === false;
259
+ default:
260
+ return false;
261
+ }
262
+ }
263
+ function getNestedValue(obj, path) {
264
+ const parts = path.split(".");
265
+ let current = obj;
266
+ for (const part of parts) {
267
+ if (current === null || current === undefined || typeof current !== "object") {
268
+ return;
269
+ }
270
+ current = current[part];
271
+ }
272
+ return current;
273
+ }
274
+
208
275
  // src/task/Task.ts
209
276
  import {
210
277
  compileSchema,
@@ -544,13 +611,13 @@ class Task {
544
611
  additionalProperties: false
545
612
  };
546
613
  }
547
- async execute(input, context) {
614
+ async execute(_input, context) {
548
615
  if (context.signal?.aborted) {
549
616
  throw new TaskAbortedError("Task aborted");
550
617
  }
551
618
  return;
552
619
  }
553
- async executeReactive(input, output, context) {
620
+ async executeReactive(_input, output, _context) {
554
621
  return output;
555
622
  }
556
623
  _runner;
@@ -912,18 +979,62 @@ class Task {
912
979
  // src/task/ConditionalTask.ts
913
980
  class ConditionalTask extends Task {
914
981
  static type = "ConditionalTask";
915
- static category = "Hidden";
916
- static title = "Conditional Task";
917
- static description = "Evaluates conditions to determine which dataflows are active";
982
+ static category = "Flow Control";
983
+ static title = "Condition";
984
+ static description = "Route data based on conditions";
918
985
  static hasDynamicSchemas = true;
919
986
  activeBranches = new Set;
987
+ buildBranchesFromConditionConfig(conditionConfig) {
988
+ if (!conditionConfig?.branches || conditionConfig.branches.length === 0) {
989
+ return [
990
+ {
991
+ id: "default",
992
+ condition: () => true,
993
+ outputPort: "1"
994
+ }
995
+ ];
996
+ }
997
+ return conditionConfig.branches.map((branch, index) => ({
998
+ id: branch.id,
999
+ outputPort: String(index + 1),
1000
+ condition: (inputData) => {
1001
+ const fieldValue = getNestedValue(inputData, branch.field);
1002
+ return evaluateCondition(fieldValue, branch.operator, branch.value);
1003
+ }
1004
+ }));
1005
+ }
1006
+ resolveBranches(input) {
1007
+ const configBranches = this.config.branches ?? [];
1008
+ if (configBranches.length > 0 && typeof configBranches[0].condition === "function") {
1009
+ return {
1010
+ branches: configBranches,
1011
+ isExclusive: this.config.exclusive ?? true,
1012
+ defaultBranch: this.config.defaultBranch,
1013
+ fromConditionConfig: false
1014
+ };
1015
+ }
1016
+ const conditionConfig = input.conditionConfig ?? this.config.extras?.conditionConfig;
1017
+ if (conditionConfig) {
1018
+ return {
1019
+ branches: this.buildBranchesFromConditionConfig(conditionConfig),
1020
+ isExclusive: conditionConfig.exclusive ?? true,
1021
+ defaultBranch: conditionConfig.defaultBranch,
1022
+ fromConditionConfig: true
1023
+ };
1024
+ }
1025
+ return {
1026
+ branches: configBranches,
1027
+ isExclusive: this.config.exclusive ?? true,
1028
+ defaultBranch: this.config.defaultBranch,
1029
+ fromConditionConfig: false
1030
+ };
1031
+ }
920
1032
  async execute(input, context) {
921
1033
  if (context.signal?.aborted) {
922
1034
  return;
923
1035
  }
924
1036
  this.activeBranches.clear();
925
- const branches = this.config.branches ?? [];
926
- const isExclusive = this.config.exclusive ?? true;
1037
+ const { branches, isExclusive, defaultBranch, fromConditionConfig } = this.resolveBranches(input);
927
1038
  for (const branch of branches) {
928
1039
  try {
929
1040
  const isActive = branch.condition(input);
@@ -937,14 +1048,50 @@ class ConditionalTask extends Task {
937
1048
  console.warn(`Condition evaluation failed for branch "${branch.id}":`, error);
938
1049
  }
939
1050
  }
940
- if (this.activeBranches.size === 0 && this.config.defaultBranch) {
941
- const defaultBranchExists = branches.some((b) => b.id === this.config.defaultBranch);
1051
+ if (this.activeBranches.size === 0 && defaultBranch) {
1052
+ const defaultBranchExists = branches.some((b) => b.id === defaultBranch);
942
1053
  if (defaultBranchExists) {
943
- this.activeBranches.add(this.config.defaultBranch);
1054
+ this.activeBranches.add(defaultBranch);
944
1055
  }
945
1056
  }
1057
+ if (fromConditionConfig) {
1058
+ return this.buildConditionConfigOutput(input, branches, isExclusive);
1059
+ }
946
1060
  return this.buildOutput(input);
947
1061
  }
1062
+ buildConditionConfigOutput(input, branches, isExclusive) {
1063
+ const output = {};
1064
+ const { conditionConfig, ...passThrough } = input;
1065
+ const inputKeys = Object.keys(passThrough);
1066
+ let matchedBranchNumber = null;
1067
+ for (let i = 0;i < branches.length; i++) {
1068
+ if (this.activeBranches.has(branches[i].id)) {
1069
+ if (matchedBranchNumber === null) {
1070
+ matchedBranchNumber = i + 1;
1071
+ }
1072
+ }
1073
+ }
1074
+ if (isExclusive) {
1075
+ if (matchedBranchNumber !== null) {
1076
+ for (const key of inputKeys) {
1077
+ output[`${key}_${matchedBranchNumber}`] = passThrough[key];
1078
+ }
1079
+ } else {
1080
+ for (const key of inputKeys) {
1081
+ output[`${key}_else`] = passThrough[key];
1082
+ }
1083
+ }
1084
+ } else {
1085
+ for (let i = 0;i < branches.length; i++) {
1086
+ if (this.activeBranches.has(branches[i].id)) {
1087
+ for (const key of inputKeys) {
1088
+ output[`${key}_${i + 1}`] = passThrough[key];
1089
+ }
1090
+ }
1091
+ }
1092
+ }
1093
+ return output;
1094
+ }
948
1095
  buildOutput(input) {
949
1096
  const output = {
950
1097
  _activeBranches: Array.from(this.activeBranches)
@@ -1202,16 +1349,18 @@ class TaskGraphRunner {
1202
1349
  await this.handleComplete();
1203
1350
  return results;
1204
1351
  }
1205
- async runGraphReactive() {
1352
+ async runGraphReactive(input = {}) {
1206
1353
  await this.handleStartReactive();
1207
1354
  const results = [];
1208
1355
  try {
1209
1356
  for await (const task of this.reactiveScheduler.tasks()) {
1357
+ const isRootTask = this.graph.getSourceDataflows(task.config.id).length === 0;
1210
1358
  if (task.status === TaskStatus.PENDING) {
1211
1359
  task.resetInputData();
1212
1360
  this.copyInputFromEdgesToNode(task);
1213
1361
  }
1214
- const taskResult = await task.runReactive();
1362
+ const taskInput = isRootTask ? input : {};
1363
+ const taskResult = await task.runReactive(taskInput);
1215
1364
  await this.pushOutputFromNodeToEdges(task, taskResult);
1216
1365
  if (this.graph.getTargetDataflows(task.config.id).length === 0) {
1217
1366
  results.push({
@@ -1512,7 +1661,7 @@ class GraphAsTaskRunner extends TaskRunner {
1512
1661
  return results;
1513
1662
  }
1514
1663
  async executeTaskChildrenReactive() {
1515
- return this.task.subGraph.runReactive();
1664
+ return this.task.subGraph.runReactive(this.task.runInputData);
1516
1665
  }
1517
1666
  async handleDisable() {
1518
1667
  if (this.task.hasChildren()) {
@@ -1545,9 +1694,9 @@ class GraphAsTaskRunner extends TaskRunner {
1545
1694
  // src/task/GraphAsTask.ts
1546
1695
  class GraphAsTask extends Task {
1547
1696
  static type = "GraphAsTask";
1548
- static title = "Graph as Task";
1549
- static description = "A task that contains a subgraph of tasks";
1550
- static category = "Hidden";
1697
+ static title = "Group";
1698
+ static description = "A group of tasks that are executed together";
1699
+ static category = "Flow Control";
1551
1700
  static compoundMerge = PROPERTY_ARRAY;
1552
1701
  static hasDynamicSchemas = true;
1553
1702
  constructor(input = {}, config = {}) {
@@ -1727,38 +1876,64 @@ class GraphAsTask extends Task {
1727
1876
  }
1728
1877
 
1729
1878
  // src/task-graph/Workflow.ts
1730
- import { EventEmitter as EventEmitter4 } from "@workglow/util";
1879
+ import { EventEmitter as EventEmitter4, uuid4 as uuid43 } from "@workglow/util";
1880
+ function CreateWorkflow(taskClass) {
1881
+ return Workflow.createWorkflow(taskClass);
1882
+ }
1883
+ function CreateLoopWorkflow(taskClass) {
1884
+ return function(config = {}) {
1885
+ return this.addLoopTask(taskClass, config);
1886
+ };
1887
+ }
1888
+ function CreateEndLoopWorkflow(methodName) {
1889
+ return function() {
1890
+ if (!this.isLoopBuilder) {
1891
+ throw new Error(`${methodName}() can only be called on loop workflows`);
1892
+ }
1893
+ return this.finalizeAndReturn();
1894
+ };
1895
+ }
1896
+
1731
1897
  class WorkflowTask extends GraphAsTask {
1732
1898
  static type = "Workflow";
1733
1899
  static compoundMerge = PROPERTY_ARRAY;
1734
1900
  }
1735
- var taskIdCounter = 0;
1736
1901
 
1737
1902
  class Workflow {
1738
- constructor(repository) {
1739
- this._repository = repository;
1740
- this._graph = new TaskGraph({
1741
- outputCache: this._repository
1742
- });
1743
- this._onChanged = this._onChanged.bind(this);
1744
- this.setupEvents();
1903
+ constructor(cache, parent, iteratorTask) {
1904
+ this._outputCache = cache;
1905
+ this._parentWorkflow = parent;
1906
+ this._iteratorTask = iteratorTask;
1907
+ this._graph = new TaskGraph({ outputCache: this._outputCache });
1908
+ if (!parent) {
1909
+ this._onChanged = this._onChanged.bind(this);
1910
+ this.setupEvents();
1911
+ }
1745
1912
  }
1746
1913
  _graph;
1747
1914
  _dataFlows = [];
1748
1915
  _error = "";
1749
- _repository;
1916
+ _outputCache;
1750
1917
  _abortController;
1918
+ _parentWorkflow;
1919
+ _iteratorTask;
1920
+ _pendingLoopConnect;
1921
+ outputCache() {
1922
+ return this._outputCache;
1923
+ }
1924
+ get isLoopBuilder() {
1925
+ return this._parentWorkflow !== undefined;
1926
+ }
1751
1927
  events = new EventEmitter4;
1752
1928
  static createWorkflow(taskClass) {
1753
1929
  const helper = function(input = {}, config = {}) {
1754
1930
  this._error = "";
1755
1931
  const parent = getLastTask(this);
1756
- taskIdCounter++;
1757
- const task = this.addTask(taskClass, input, { id: String(taskIdCounter), ...config });
1932
+ const task = this.addTaskToGraph(taskClass, input, { id: uuid43(), ...config });
1758
1933
  if (this._dataFlows.length > 0) {
1759
1934
  this._dataFlows.forEach((dataflow) => {
1760
1935
  const taskSchema = task.inputSchema();
1761
- if (typeof taskSchema !== "boolean" && taskSchema.properties?.[dataflow.targetTaskPortId] === undefined || taskSchema === true && dataflow.targetTaskPortId !== DATAFLOW_ALL_PORTS) {
1936
+ if (typeof taskSchema !== "boolean" && taskSchema.properties?.[dataflow.targetTaskPortId] === undefined && taskSchema.additionalProperties !== true || taskSchema === true && dataflow.targetTaskPortId !== DATAFLOW_ALL_PORTS) {
1762
1937
  this._error = `Input ${dataflow.targetTaskPortId} not found on task ${task.config.id}`;
1763
1938
  console.error(this._error);
1764
1939
  return;
@@ -1769,158 +1944,23 @@ class Workflow {
1769
1944
  this._dataFlows = [];
1770
1945
  }
1771
1946
  if (parent && this.graph.getTargetDataflows(parent.config.id).length === 0) {
1772
- const matches = new Map;
1773
- const sourceSchema = parent.outputSchema();
1774
- const targetSchema = task.inputSchema();
1775
- const makeMatch = (comparator) => {
1776
- if (typeof sourceSchema === "object") {
1777
- if (targetSchema === true || typeof targetSchema === "object" && targetSchema.additionalProperties === true) {
1778
- for (const fromOutputPortId of Object.keys(sourceSchema.properties || {})) {
1779
- matches.set(fromOutputPortId, fromOutputPortId);
1780
- this.connect(parent.config.id, fromOutputPortId, task.config.id, fromOutputPortId);
1781
- }
1782
- return matches;
1783
- }
1784
- }
1785
- if (typeof sourceSchema === "boolean" || typeof targetSchema === "boolean") {
1786
- return matches;
1787
- }
1788
- for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(sourceSchema.properties || {})) {
1789
- for (const [toInputPortId, toPortInputSchema] of Object.entries(targetSchema.properties || {})) {
1790
- if (!matches.has(toInputPortId) && comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
1791
- matches.set(toInputPortId, fromOutputPortId);
1792
- this.connect(parent.config.id, fromOutputPortId, task.config.id, toInputPortId);
1793
- }
1794
- }
1795
- }
1796
- return matches;
1797
- };
1798
- const getSpecificTypeIdentifiers = (schema) => {
1799
- const formats = new Set;
1800
- const ids = new Set;
1801
- if (typeof schema === "boolean") {
1802
- return { formats, ids };
1803
- }
1804
- const extractFromSchema = (s) => {
1805
- if (!s || typeof s !== "object" || Array.isArray(s))
1806
- return;
1807
- if (s.format)
1808
- formats.add(s.format);
1809
- if (s.$id)
1810
- ids.add(s.$id);
1811
- };
1812
- extractFromSchema(schema);
1813
- const checkUnion = (schemas) => {
1814
- if (!schemas)
1815
- return;
1816
- for (const s of schemas) {
1817
- if (typeof s === "boolean")
1818
- continue;
1819
- extractFromSchema(s);
1820
- if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
1821
- extractFromSchema(s.items);
1822
- }
1823
- }
1824
- };
1825
- checkUnion(schema.oneOf);
1826
- checkUnion(schema.anyOf);
1827
- if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
1828
- extractFromSchema(schema.items);
1829
- }
1830
- return { formats, ids };
1831
- };
1832
- const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
1833
- if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
1834
- return fromPortOutputSchema === true && toPortInputSchema === true;
1835
- }
1836
- const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
1837
- const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
1838
- for (const format of outputIds.formats) {
1839
- if (inputIds.formats.has(format)) {
1840
- return true;
1841
- }
1842
- }
1843
- for (const id of outputIds.ids) {
1844
- if (inputIds.ids.has(id)) {
1845
- return true;
1846
- }
1847
- }
1848
- if (requireSpecificType) {
1849
- return false;
1850
- }
1851
- const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
1852
- if (!idTypeBlank)
1853
- return false;
1854
- if (fromPortOutputSchema.type === toPortInputSchema.type)
1855
- return true;
1856
- const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
1857
- if (typeof schema === "boolean")
1858
- return schema;
1859
- return schema.type === fromPortOutputSchema.type;
1860
- }) ?? false;
1861
- const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
1862
- if (typeof schema === "boolean")
1863
- return schema;
1864
- return schema.type === fromPortOutputSchema.type;
1865
- }) ?? false;
1866
- return matchesOneOf || matchesAnyOf;
1867
- };
1868
- makeMatch(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
1869
- const outputPortIdMatch = fromOutputPortId === toInputPortId;
1870
- const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
1871
- const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
1872
- return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
1873
- });
1874
- makeMatch(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
1875
- return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
1876
- });
1877
- const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
1878
- const providedInputKeys = new Set(Object.keys(input || {}));
1879
- const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r));
1880
- let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
1881
- if (unmatchedRequired.length > 0) {
1882
- const nodes = this._graph.getTasks();
1883
- const parentIndex = nodes.findIndex((n) => n.config.id === parent.config.id);
1884
- for (let i = parentIndex - 1;i >= 0 && unmatchedRequired.length > 0; i--) {
1885
- const earlierTask = nodes[i];
1886
- const earlierOutputSchema = earlierTask.outputSchema();
1887
- const makeMatchFromEarlier = (comparator) => {
1888
- if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
1889
- return;
1890
- }
1891
- for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
1892
- for (const requiredInputId of unmatchedRequired) {
1893
- const toPortInputSchema = targetSchema.properties?.[requiredInputId];
1894
- if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
1895
- matches.set(requiredInputId, fromOutputPortId);
1896
- this.connect(earlierTask.config.id, fromOutputPortId, task.config.id, requiredInputId);
1897
- }
1898
- }
1899
- }
1900
- };
1901
- makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
1902
- const outputPortIdMatch = fromOutputPortId === toInputPortId;
1903
- const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
1904
- const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
1905
- return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
1906
- });
1907
- makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
1908
- return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
1909
- });
1910
- unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
1911
- }
1947
+ const nodes = this._graph.getTasks();
1948
+ const parentIndex = nodes.findIndex((n) => n.config.id === parent.config.id);
1949
+ const earlierTasks = [];
1950
+ for (let i = parentIndex - 1;i >= 0; i--) {
1951
+ earlierTasks.push(nodes[i]);
1912
1952
  }
1913
- const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
1914
- if (stillUnmatchedRequired.length > 0) {
1915
- 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.`;
1916
- console.error(this._error);
1917
- this.graph.removeTask(task.config.id);
1918
- } else if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
1919
- const hasRequiredInputs = requiredInputs.size > 0;
1920
- const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
1921
- const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
1922
- if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
1923
- 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.`;
1953
+ const providedInputKeys = new Set(Object.keys(input || {}));
1954
+ const result = Workflow.autoConnect(this.graph, parent, task, {
1955
+ providedInputKeys,
1956
+ earlierTasks
1957
+ });
1958
+ if (result.error) {
1959
+ if (this.isLoopBuilder) {
1960
+ this._error = result.error;
1961
+ console.warn(this._error);
1962
+ } else {
1963
+ this._error = result.error + " Task not added.";
1924
1964
  console.error(this._error);
1925
1965
  this.graph.removeTask(task.config.id);
1926
1966
  }
@@ -1963,12 +2003,20 @@ class Workflow {
1963
2003
  return this.events.waitOn(name);
1964
2004
  }
1965
2005
  async run(input = {}) {
2006
+ if (this.isLoopBuilder) {
2007
+ this.finalizeTemplate();
2008
+ if (this._pendingLoopConnect) {
2009
+ this._parentWorkflow.autoConnectLoopTask(this._pendingLoopConnect);
2010
+ this._pendingLoopConnect = undefined;
2011
+ }
2012
+ return this._parentWorkflow.run(input);
2013
+ }
1966
2014
  this.events.emit("start");
1967
2015
  this._abortController = new AbortController;
1968
2016
  try {
1969
2017
  const output = await this.graph.run(input, {
1970
2018
  parentSignal: this._abortController.signal,
1971
- outputCache: this._repository
2019
+ outputCache: this._outputCache
1972
2020
  });
1973
2021
  const results = this.graph.mergeExecuteOutputsToRunOutput(output, PROPERTY_ARRAY);
1974
2022
  this.events.emit("complete");
@@ -1981,6 +2029,9 @@ class Workflow {
1981
2029
  }
1982
2030
  }
1983
2031
  async abort() {
2032
+ if (this._parentWorkflow) {
2033
+ return this._parentWorkflow.abort();
2034
+ }
1984
2035
  this._abortController?.abort();
1985
2036
  }
1986
2037
  pop() {
@@ -2049,10 +2100,12 @@ class Workflow {
2049
2100
  return task;
2050
2101
  }
2051
2102
  reset() {
2052
- taskIdCounter = 0;
2103
+ if (this._parentWorkflow) {
2104
+ throw new WorkflowError("Cannot reset a loop workflow. Call reset() on the parent workflow.");
2105
+ }
2053
2106
  this.clearEvents();
2054
2107
  this._graph = new TaskGraph({
2055
- outputCache: this._repository
2108
+ outputCache: this._outputCache
2056
2109
  });
2057
2110
  this._dataFlows = [];
2058
2111
  this._error = "";
@@ -2107,15 +2160,243 @@ class Workflow {
2107
2160
  this.graph.addDataflow(dataflow);
2108
2161
  return this;
2109
2162
  }
2110
- addTask(taskClass, input, config) {
2163
+ addTaskToGraph(taskClass, input, config) {
2111
2164
  const task = new taskClass(input, config);
2112
2165
  const id = this.graph.addTask(task);
2113
2166
  this.events.emit("changed", id);
2114
2167
  return task;
2115
2168
  }
2116
- }
2117
- function CreateWorkflow(taskClass) {
2118
- return Workflow.createWorkflow(taskClass);
2169
+ addTask(taskClass, input, config) {
2170
+ const helper = Workflow.createWorkflow(taskClass);
2171
+ return helper.call(this, input, config);
2172
+ }
2173
+ addLoopTask(taskClass, config = {}) {
2174
+ this._error = "";
2175
+ const parent = getLastTask(this);
2176
+ const task = this.addTaskToGraph(taskClass, {}, { id: uuid43(), ...config });
2177
+ if (this._dataFlows.length > 0) {
2178
+ this._dataFlows.forEach((dataflow) => {
2179
+ const taskSchema = task.inputSchema();
2180
+ if (typeof taskSchema !== "boolean" && taskSchema.properties?.[dataflow.targetTaskPortId] === undefined && taskSchema.additionalProperties !== true || taskSchema === true && dataflow.targetTaskPortId !== DATAFLOW_ALL_PORTS) {
2181
+ this._error = `Input ${dataflow.targetTaskPortId} not found on task ${task.config.id}`;
2182
+ console.error(this._error);
2183
+ return;
2184
+ }
2185
+ dataflow.targetTaskId = task.config.id;
2186
+ this.graph.addDataflow(dataflow);
2187
+ });
2188
+ this._dataFlows = [];
2189
+ }
2190
+ const loopBuilder = new Workflow(this.outputCache(), this, task);
2191
+ if (parent) {
2192
+ loopBuilder._pendingLoopConnect = { parent, iteratorTask: task };
2193
+ }
2194
+ return loopBuilder;
2195
+ }
2196
+ autoConnectLoopTask(pending) {
2197
+ if (!pending)
2198
+ return;
2199
+ const { parent, iteratorTask } = pending;
2200
+ if (this.graph.getTargetDataflows(parent.config.id).length === 0) {
2201
+ const nodes = this._graph.getTasks();
2202
+ const parentIndex = nodes.findIndex((n) => n.config.id === parent.config.id);
2203
+ const earlierTasks = [];
2204
+ for (let i = parentIndex - 1;i >= 0; i--) {
2205
+ earlierTasks.push(nodes[i]);
2206
+ }
2207
+ const result = Workflow.autoConnect(this.graph, parent, iteratorTask, {
2208
+ earlierTasks
2209
+ });
2210
+ if (result.error) {
2211
+ this._error = result.error + " Task not added.";
2212
+ console.error(this._error);
2213
+ this.graph.removeTask(iteratorTask.config.id);
2214
+ }
2215
+ }
2216
+ }
2217
+ static AutoConnectOptions = Symbol("AutoConnectOptions");
2218
+ static autoConnect(graph, sourceTask, targetTask, options) {
2219
+ const matches = new Map;
2220
+ const sourceSchema = sourceTask.outputSchema();
2221
+ const targetSchema = targetTask.inputSchema();
2222
+ const providedInputKeys = options?.providedInputKeys ?? new Set;
2223
+ const earlierTasks = options?.earlierTasks ?? [];
2224
+ const getSpecificTypeIdentifiers = (schema) => {
2225
+ const formats = new Set;
2226
+ const ids = new Set;
2227
+ if (typeof schema === "boolean") {
2228
+ return { formats, ids };
2229
+ }
2230
+ const extractFromSchema = (s) => {
2231
+ if (!s || typeof s !== "object" || Array.isArray(s))
2232
+ return;
2233
+ if (s.format)
2234
+ formats.add(s.format);
2235
+ if (s.$id)
2236
+ ids.add(s.$id);
2237
+ };
2238
+ extractFromSchema(schema);
2239
+ const checkUnion = (schemas) => {
2240
+ if (!schemas)
2241
+ return;
2242
+ for (const s of schemas) {
2243
+ if (typeof s === "boolean")
2244
+ continue;
2245
+ extractFromSchema(s);
2246
+ if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
2247
+ extractFromSchema(s.items);
2248
+ }
2249
+ }
2250
+ };
2251
+ checkUnion(schema.oneOf);
2252
+ checkUnion(schema.anyOf);
2253
+ if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
2254
+ extractFromSchema(schema.items);
2255
+ }
2256
+ return { formats, ids };
2257
+ };
2258
+ const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
2259
+ if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
2260
+ return fromPortOutputSchema === true && toPortInputSchema === true;
2261
+ }
2262
+ const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
2263
+ const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
2264
+ for (const format of outputIds.formats) {
2265
+ if (inputIds.formats.has(format)) {
2266
+ return true;
2267
+ }
2268
+ }
2269
+ for (const id of outputIds.ids) {
2270
+ if (inputIds.ids.has(id)) {
2271
+ return true;
2272
+ }
2273
+ }
2274
+ if (requireSpecificType) {
2275
+ return false;
2276
+ }
2277
+ const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
2278
+ if (!idTypeBlank)
2279
+ return false;
2280
+ if (fromPortOutputSchema.type === toPortInputSchema.type)
2281
+ return true;
2282
+ const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
2283
+ if (typeof schema === "boolean")
2284
+ return schema;
2285
+ return schema.type === fromPortOutputSchema.type;
2286
+ }) ?? false;
2287
+ const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
2288
+ if (typeof schema === "boolean")
2289
+ return schema;
2290
+ return schema.type === fromPortOutputSchema.type;
2291
+ }) ?? false;
2292
+ return matchesOneOf || matchesAnyOf;
2293
+ };
2294
+ const makeMatch = (fromSchema, toSchema, fromTaskId, toTaskId, comparator) => {
2295
+ if (typeof fromSchema === "object") {
2296
+ if (toSchema === true || typeof toSchema === "object" && toSchema.additionalProperties === true) {
2297
+ for (const fromOutputPortId of Object.keys(fromSchema.properties || {})) {
2298
+ matches.set(fromOutputPortId, fromOutputPortId);
2299
+ graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
2300
+ }
2301
+ return;
2302
+ }
2303
+ }
2304
+ if (typeof fromSchema === "boolean" || typeof toSchema === "boolean") {
2305
+ return;
2306
+ }
2307
+ for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(fromSchema.properties || {})) {
2308
+ for (const [toInputPortId, toPortInputSchema] of Object.entries(toSchema.properties || {})) {
2309
+ if (!matches.has(toInputPortId) && comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
2310
+ matches.set(toInputPortId, fromOutputPortId);
2311
+ graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, toInputPortId));
2312
+ }
2313
+ }
2314
+ }
2315
+ };
2316
+ makeMatch(sourceSchema, targetSchema, sourceTask.config.id, targetTask.config.id, ([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
2317
+ const outputPortIdMatch = fromOutputPortId === toInputPortId;
2318
+ const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
2319
+ const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
2320
+ return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
2321
+ });
2322
+ makeMatch(sourceSchema, targetSchema, sourceTask.config.id, targetTask.config.id, ([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
2323
+ return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
2324
+ });
2325
+ const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
2326
+ const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r));
2327
+ let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
2328
+ if (unmatchedRequired.length > 0 && earlierTasks.length > 0) {
2329
+ for (let i = 0;i < earlierTasks.length && unmatchedRequired.length > 0; i++) {
2330
+ const earlierTask = earlierTasks[i];
2331
+ const earlierOutputSchema = earlierTask.outputSchema();
2332
+ const makeMatchFromEarlier = (comparator) => {
2333
+ if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
2334
+ return;
2335
+ }
2336
+ for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
2337
+ for (const requiredInputId of unmatchedRequired) {
2338
+ const toPortInputSchema = targetSchema.properties?.[requiredInputId];
2339
+ if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
2340
+ matches.set(requiredInputId, fromOutputPortId);
2341
+ graph.addDataflow(new Dataflow(earlierTask.config.id, fromOutputPortId, targetTask.config.id, requiredInputId));
2342
+ }
2343
+ }
2344
+ }
2345
+ };
2346
+ makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
2347
+ const outputPortIdMatch = fromOutputPortId === toInputPortId;
2348
+ const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
2349
+ const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
2350
+ return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
2351
+ });
2352
+ makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
2353
+ return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
2354
+ });
2355
+ unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
2356
+ }
2357
+ }
2358
+ const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
2359
+ if (stillUnmatchedRequired.length > 0) {
2360
+ return {
2361
+ matches,
2362
+ error: `Could not find matches for required inputs [${stillUnmatchedRequired.join(", ")}] of ${targetTask.type}. ` + `Attempted to match from ${sourceTask.type} and earlier tasks.`,
2363
+ unmatchedRequired: stillUnmatchedRequired
2364
+ };
2365
+ }
2366
+ if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
2367
+ const hasRequiredInputs = requiredInputs.size > 0;
2368
+ const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
2369
+ const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
2370
+ if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
2371
+ return {
2372
+ matches,
2373
+ 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.`,
2374
+ unmatchedRequired: []
2375
+ };
2376
+ }
2377
+ }
2378
+ return {
2379
+ matches,
2380
+ unmatchedRequired: []
2381
+ };
2382
+ }
2383
+ finalizeTemplate() {
2384
+ if (!this._iteratorTask || this.graph.getTasks().length === 0) {
2385
+ return;
2386
+ }
2387
+ this._iteratorTask.subGraph = this.graph;
2388
+ }
2389
+ finalizeAndReturn() {
2390
+ if (!this._parentWorkflow) {
2391
+ throw new WorkflowError("finalizeAndReturn() can only be called on loop workflows");
2392
+ }
2393
+ this.finalizeTemplate();
2394
+ if (this._pendingLoopConnect) {
2395
+ this._parentWorkflow.autoConnectLoopTask(this._pendingLoopConnect);
2396
+ this._pendingLoopConnect = undefined;
2397
+ }
2398
+ return this._parentWorkflow;
2399
+ }
2119
2400
  }
2120
2401
 
2121
2402
  // src/task-graph/Conversions.ts
@@ -2283,8 +2564,8 @@ class TaskGraph {
2283
2564
  parentSignal: config?.parentSignal || undefined
2284
2565
  });
2285
2566
  }
2286
- runReactive() {
2287
- return this.runner.runGraphReactive();
2567
+ runReactive(input = {}) {
2568
+ return this.runner.runGraphReactive(input);
2288
2569
  }
2289
2570
  mergeExecuteOutputsToRunOutput(results, compoundMerge) {
2290
2571
  return this.runner.mergeExecuteOutputsToRunOutput(results, compoundMerge);
@@ -2355,7 +2636,7 @@ class TaskGraph {
2355
2636
  return this._dag.removeNode(taskId);
2356
2637
  }
2357
2638
  resetGraph() {
2358
- this.runner.resetGraph(this, uuid43());
2639
+ this.runner.resetGraph(this, uuid44());
2359
2640
  }
2360
2641
  toJSON() {
2361
2642
  const tasks = this.getTasks().map((node) => node.toJSON());
@@ -2520,80 +2801,1144 @@ function serialGraph(tasks, inputHandle, outputHandle) {
2520
2801
  graph.addDataflows(serialGraphEdges(tasks, inputHandle, outputHandle));
2521
2802
  return graph;
2522
2803
  }
2523
- // src/task/JobQueueFactory.ts
2524
- import {
2525
- JobQueueClient,
2526
- JobQueueServer
2527
- } from "@workglow/job-queue";
2528
- import { InMemoryQueueStorage } from "@workglow/storage";
2529
- import { createServiceToken as createServiceToken2, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
2530
- var JOB_QUEUE_FACTORY = createServiceToken2("taskgraph.jobQueueFactory");
2531
- var defaultJobQueueFactory = async ({
2532
- queueName,
2533
- jobClass,
2534
- options
2535
- }) => {
2536
- const storage = options?.storage ?? new InMemoryQueueStorage(queueName);
2537
- await storage.setupDatabase();
2538
- const server = new JobQueueServer(jobClass, {
2539
- storage,
2540
- queueName,
2541
- limiter: options?.limiter,
2542
- workerCount: options?.workerCount,
2543
- pollIntervalMs: options?.pollIntervalMs,
2544
- deleteAfterCompletionMs: options?.deleteAfterCompletionMs,
2545
- deleteAfterFailureMs: options?.deleteAfterFailureMs,
2546
- deleteAfterDisabledMs: options?.deleteAfterDisabledMs,
2547
- cleanupIntervalMs: options?.cleanupIntervalMs
2548
- });
2549
- const client = new JobQueueClient({
2550
- storage,
2551
- queueName
2552
- });
2553
- client.attach(server);
2554
- return { server, client, storage };
2555
- };
2556
- function registerJobQueueFactory(factory) {
2557
- globalServiceRegistry3.registerInstance(JOB_QUEUE_FACTORY, factory);
2558
- }
2559
- function createJobQueueFactoryWithOptions(defaultOptions = {}) {
2560
- return async ({
2561
- queueName,
2562
- jobClass,
2563
- options
2564
- }) => {
2565
- const mergedOptions = {
2566
- ...defaultOptions,
2567
- ...options ?? {}
2568
- };
2569
- const storage = mergedOptions.storage ?? new InMemoryQueueStorage(queueName);
2570
- await storage.setupDatabase();
2571
- const server = new JobQueueServer(jobClass, {
2572
- storage,
2573
- queueName,
2574
- limiter: mergedOptions.limiter,
2575
- workerCount: mergedOptions.workerCount,
2576
- pollIntervalMs: mergedOptions.pollIntervalMs,
2577
- deleteAfterCompletionMs: mergedOptions.deleteAfterCompletionMs,
2578
- deleteAfterFailureMs: mergedOptions.deleteAfterFailureMs,
2579
- deleteAfterDisabledMs: mergedOptions.deleteAfterDisabledMs,
2580
- cleanupIntervalMs: mergedOptions.cleanupIntervalMs
2581
- });
2582
- const client = new JobQueueClient({
2583
- storage,
2584
- queueName
2585
- });
2586
- client.attach(server);
2587
- return { server, client, storage };
2588
- };
2589
- }
2590
- function getJobQueueFactory() {
2591
- if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
2592
- registerJobQueueFactory(defaultJobQueueFactory);
2804
+ // src/task/IteratorTaskRunner.ts
2805
+ class IteratorTaskRunner extends GraphAsTaskRunner {
2806
+ subGraphRunChain = Promise.resolve();
2807
+ async executeTask(input) {
2808
+ const analysis = this.task.analyzeIterationInput(input);
2809
+ if (analysis.iterationCount === 0) {
2810
+ const emptyResult = this.task.getEmptyResult();
2811
+ return this.executeTaskReactive(input, emptyResult);
2812
+ }
2813
+ const result = this.task.isReduceTask() ? await this.executeReduceIterations(analysis) : await this.executeCollectIterations(analysis);
2814
+ return this.executeTaskReactive(input, result);
2593
2815
  }
2594
- return globalServiceRegistry3.get(JOB_QUEUE_FACTORY);
2595
- }
2596
- if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
2816
+ async executeTaskReactive(input, output) {
2817
+ const reactiveResult = await this.task.executeReactive(input, output, { own: this.own });
2818
+ return Object.assign({}, output, reactiveResult ?? {});
2819
+ }
2820
+ async executeCollectIterations(analysis) {
2821
+ const iterationCount = analysis.iterationCount;
2822
+ const preserveOrder = this.task.preserveIterationOrder();
2823
+ const batchSize = this.task.batchSize !== undefined && this.task.batchSize > 0 ? this.task.batchSize : iterationCount;
2824
+ const requestedConcurrency = this.task.concurrencyLimit ?? iterationCount;
2825
+ const concurrency = Math.max(1, Math.min(requestedConcurrency, iterationCount));
2826
+ const orderedResults = preserveOrder ? new Array(iterationCount) : [];
2827
+ const completionOrderResults = [];
2828
+ for (let batchStart = 0;batchStart < iterationCount; batchStart += batchSize) {
2829
+ if (this.abortController?.signal.aborted) {
2830
+ break;
2831
+ }
2832
+ const batchEnd = Math.min(batchStart + batchSize, iterationCount);
2833
+ const batchIndices = Array.from({ length: batchEnd - batchStart }, (_, i) => batchStart + i);
2834
+ const batchResults = await this.executeBatch(batchIndices, analysis, iterationCount, concurrency);
2835
+ for (const { index, result } of batchResults) {
2836
+ if (result === undefined)
2837
+ continue;
2838
+ if (preserveOrder) {
2839
+ orderedResults[index] = result;
2840
+ } else {
2841
+ completionOrderResults.push(result);
2842
+ }
2843
+ }
2844
+ const progress = Math.round(batchEnd / iterationCount * 100);
2845
+ await this.handleProgress(progress, `Completed ${batchEnd}/${iterationCount} iterations`);
2846
+ }
2847
+ const collected = preserveOrder ? orderedResults.filter((result) => result !== undefined) : completionOrderResults;
2848
+ return this.task.collectResults(collected);
2849
+ }
2850
+ async executeReduceIterations(analysis) {
2851
+ const iterationCount = analysis.iterationCount;
2852
+ let accumulator = this.task.getInitialAccumulator();
2853
+ for (let index = 0;index < iterationCount; index++) {
2854
+ if (this.abortController?.signal.aborted) {
2855
+ break;
2856
+ }
2857
+ const iterationInput = this.task.buildIterationRunInput(analysis, index, iterationCount, {
2858
+ accumulator
2859
+ });
2860
+ const iterationResult = await this.executeSubgraphIteration(iterationInput);
2861
+ accumulator = this.task.mergeIterationIntoAccumulator(accumulator, iterationResult, index);
2862
+ const progress = Math.round((index + 1) / iterationCount * 100);
2863
+ await this.handleProgress(progress, `Completed ${index + 1}/${iterationCount} iterations`);
2864
+ }
2865
+ return accumulator;
2866
+ }
2867
+ async executeBatch(indices, analysis, iterationCount, concurrency) {
2868
+ const results = [];
2869
+ let cursor = 0;
2870
+ const workerCount = Math.max(1, Math.min(concurrency, indices.length));
2871
+ const workers = Array.from({ length: workerCount }, async () => {
2872
+ while (true) {
2873
+ if (this.abortController?.signal.aborted) {
2874
+ return;
2875
+ }
2876
+ const position = cursor;
2877
+ cursor += 1;
2878
+ if (position >= indices.length) {
2879
+ return;
2880
+ }
2881
+ const index = indices[position];
2882
+ const iterationInput = this.task.buildIterationRunInput(analysis, index, iterationCount);
2883
+ const result = await this.executeSubgraphIteration(iterationInput);
2884
+ results.push({ index, result });
2885
+ }
2886
+ });
2887
+ await Promise.all(workers);
2888
+ return results;
2889
+ }
2890
+ async executeSubgraphIteration(input) {
2891
+ let releaseTurn;
2892
+ const waitForPreviousRun = this.subGraphRunChain;
2893
+ this.subGraphRunChain = new Promise((resolve) => {
2894
+ releaseTurn = resolve;
2895
+ });
2896
+ await waitForPreviousRun;
2897
+ try {
2898
+ if (this.abortController?.signal.aborted) {
2899
+ return;
2900
+ }
2901
+ const results = await this.task.subGraph.run(input, {
2902
+ parentSignal: this.abortController?.signal,
2903
+ outputCache: this.outputCache
2904
+ });
2905
+ if (results.length === 0) {
2906
+ return;
2907
+ }
2908
+ return this.task.subGraph.mergeExecuteOutputsToRunOutput(results, this.task.compoundMerge);
2909
+ } finally {
2910
+ releaseTurn?.();
2911
+ }
2912
+ }
2913
+ }
2914
+
2915
+ // src/task/IteratorTask.ts
2916
+ var ITERATOR_CONTEXT_SCHEMA = {
2917
+ type: "object",
2918
+ properties: {
2919
+ _iterationIndex: {
2920
+ type: "integer",
2921
+ minimum: 0,
2922
+ title: "Iteration Index",
2923
+ description: "Current iteration index (0-based)",
2924
+ "x-ui-iteration": true
2925
+ },
2926
+ _iterationCount: {
2927
+ type: "integer",
2928
+ minimum: 0,
2929
+ title: "Iteration Count",
2930
+ description: "Total number of iterations",
2931
+ "x-ui-iteration": true
2932
+ }
2933
+ }
2934
+ };
2935
+ function isArrayVariant(schema) {
2936
+ if (!schema || typeof schema !== "object")
2937
+ return false;
2938
+ const record = schema;
2939
+ return record.type === "array" || record.items !== undefined;
2940
+ }
2941
+ function getExplicitIterationFlag(schema) {
2942
+ if (!schema || typeof schema !== "object")
2943
+ return;
2944
+ const record = schema;
2945
+ const flag = record["x-ui-iteration"];
2946
+ if (flag === true)
2947
+ return true;
2948
+ if (flag === false)
2949
+ return false;
2950
+ return;
2951
+ }
2952
+ function inferIterationFromSchema(schema) {
2953
+ if (!schema || typeof schema !== "object")
2954
+ return;
2955
+ const record = schema;
2956
+ if (record.type === "array" || record.items !== undefined) {
2957
+ return true;
2958
+ }
2959
+ const variants = record.oneOf ?? record.anyOf;
2960
+ if (!Array.isArray(variants) || variants.length === 0) {
2961
+ if (record.type !== undefined) {
2962
+ return false;
2963
+ }
2964
+ return;
2965
+ }
2966
+ let hasArrayVariant = false;
2967
+ let hasNonArrayVariant = false;
2968
+ for (const variant of variants) {
2969
+ if (isArrayVariant(variant)) {
2970
+ hasArrayVariant = true;
2971
+ } else {
2972
+ hasNonArrayVariant = true;
2973
+ }
2974
+ }
2975
+ if (hasArrayVariant && hasNonArrayVariant)
2976
+ return;
2977
+ if (hasArrayVariant)
2978
+ return true;
2979
+ return false;
2980
+ }
2981
+ function createFlexibleSchema(baseSchema) {
2982
+ if (typeof baseSchema === "boolean")
2983
+ return baseSchema;
2984
+ return {
2985
+ anyOf: [baseSchema, { type: "array", items: baseSchema }]
2986
+ };
2987
+ }
2988
+ function createArraySchema(baseSchema) {
2989
+ if (typeof baseSchema === "boolean")
2990
+ return baseSchema;
2991
+ return {
2992
+ type: "array",
2993
+ items: baseSchema
2994
+ };
2995
+ }
2996
+ function extractBaseSchema(schema) {
2997
+ if (typeof schema === "boolean")
2998
+ return schema;
2999
+ const schemaType = schema.type;
3000
+ if (schemaType === "array" && schema.items) {
3001
+ return schema.items;
3002
+ }
3003
+ const variants = schema.oneOf ?? schema.anyOf;
3004
+ if (Array.isArray(variants)) {
3005
+ for (const variant of variants) {
3006
+ if (typeof variant === "object") {
3007
+ const variantType = variant.type;
3008
+ if (variantType !== "array") {
3009
+ return variant;
3010
+ }
3011
+ }
3012
+ }
3013
+ for (const variant of variants) {
3014
+ if (typeof variant === "object") {
3015
+ const variantType = variant.type;
3016
+ if (variantType === "array" && variant.items) {
3017
+ return variant.items;
3018
+ }
3019
+ }
3020
+ }
3021
+ }
3022
+ return schema;
3023
+ }
3024
+ function schemaAcceptsArray(schema) {
3025
+ if (typeof schema === "boolean")
3026
+ return false;
3027
+ const schemaType = schema.type;
3028
+ if (schemaType === "array")
3029
+ return true;
3030
+ const variants = schema.oneOf ?? schema.anyOf;
3031
+ if (Array.isArray(variants)) {
3032
+ return variants.some((variant) => isArrayVariant(variant));
3033
+ }
3034
+ return false;
3035
+ }
3036
+
3037
+ class IteratorTask extends GraphAsTask {
3038
+ static type = "IteratorTask";
3039
+ static category = "Flow Control";
3040
+ static title = "Iterator";
3041
+ static description = "Base class for loop-type tasks";
3042
+ static hasDynamicSchemas = true;
3043
+ static getIterationContextSchema() {
3044
+ return ITERATOR_CONTEXT_SCHEMA;
3045
+ }
3046
+ _iteratorPortInfo;
3047
+ _iterationInputSchema;
3048
+ constructor(input = {}, config = {}) {
3049
+ super(input, config);
3050
+ }
3051
+ get runner() {
3052
+ if (!this._runner) {
3053
+ this._runner = new IteratorTaskRunner(this);
3054
+ }
3055
+ return this._runner;
3056
+ }
3057
+ set subGraph(subGraph) {
3058
+ super.subGraph = subGraph;
3059
+ this.invalidateIterationInputSchema();
3060
+ this.events.emit("regenerate");
3061
+ }
3062
+ get subGraph() {
3063
+ return super.subGraph;
3064
+ }
3065
+ regenerateGraph() {
3066
+ this.invalidateIterationInputSchema();
3067
+ super.regenerateGraph();
3068
+ }
3069
+ preserveIterationOrder() {
3070
+ return true;
3071
+ }
3072
+ isReduceTask() {
3073
+ return false;
3074
+ }
3075
+ getInitialAccumulator() {
3076
+ return {};
3077
+ }
3078
+ buildIterationRunInput(analysis, index, iterationCount, extraInput = {}) {
3079
+ return {
3080
+ ...analysis.getIterationInput(index),
3081
+ ...extraInput,
3082
+ _iterationIndex: index,
3083
+ _iterationCount: iterationCount
3084
+ };
3085
+ }
3086
+ mergeIterationIntoAccumulator(accumulator, iterationResult, _index) {
3087
+ return iterationResult ?? accumulator;
3088
+ }
3089
+ getEmptyResult() {
3090
+ return {};
3091
+ }
3092
+ collectResults(results) {
3093
+ if (results.length === 0) {
3094
+ return {};
3095
+ }
3096
+ const merged = {};
3097
+ for (const result of results) {
3098
+ if (!result || typeof result !== "object")
3099
+ continue;
3100
+ for (const [key, value] of Object.entries(result)) {
3101
+ if (!merged[key]) {
3102
+ merged[key] = [];
3103
+ }
3104
+ merged[key].push(value);
3105
+ }
3106
+ }
3107
+ return merged;
3108
+ }
3109
+ get concurrencyLimit() {
3110
+ return this.config.concurrencyLimit;
3111
+ }
3112
+ get batchSize() {
3113
+ return this.config.batchSize;
3114
+ }
3115
+ get iterationInputConfig() {
3116
+ return this.config.iterationInputConfig;
3117
+ }
3118
+ buildDefaultIterationInputSchema() {
3119
+ const innerSchema = this.getInnerInputSchema();
3120
+ if (!innerSchema || typeof innerSchema === "boolean") {
3121
+ return { type: "object", properties: {}, additionalProperties: true };
3122
+ }
3123
+ const properties = {};
3124
+ const innerProps = innerSchema.properties || {};
3125
+ for (const [key, propSchema] of Object.entries(innerProps)) {
3126
+ if (typeof propSchema === "boolean")
3127
+ continue;
3128
+ if (propSchema["x-ui-iteration"]) {
3129
+ continue;
3130
+ }
3131
+ const baseSchema = propSchema;
3132
+ properties[key] = createFlexibleSchema(baseSchema);
3133
+ }
3134
+ return {
3135
+ type: "object",
3136
+ properties,
3137
+ additionalProperties: innerSchema.additionalProperties ?? true
3138
+ };
3139
+ }
3140
+ buildConfiguredIterationInputSchema() {
3141
+ const innerSchema = this.getInnerInputSchema();
3142
+ if (!innerSchema || typeof innerSchema === "boolean") {
3143
+ return { type: "object", properties: {}, additionalProperties: true };
3144
+ }
3145
+ const config = this.iterationInputConfig || {};
3146
+ const properties = {};
3147
+ const innerProps = innerSchema.properties || {};
3148
+ for (const [key, propSchema] of Object.entries(innerProps)) {
3149
+ if (typeof propSchema === "boolean")
3150
+ continue;
3151
+ if (propSchema["x-ui-iteration"]) {
3152
+ continue;
3153
+ }
3154
+ const baseSchema = propSchema;
3155
+ const propConfig = config[key];
3156
+ if (!propConfig) {
3157
+ properties[key] = createFlexibleSchema(baseSchema);
3158
+ continue;
3159
+ }
3160
+ switch (propConfig.mode) {
3161
+ case "array":
3162
+ properties[key] = createArraySchema(propConfig.baseSchema);
3163
+ break;
3164
+ case "scalar":
3165
+ properties[key] = propConfig.baseSchema;
3166
+ break;
3167
+ case "flexible":
3168
+ default:
3169
+ properties[key] = createFlexibleSchema(propConfig.baseSchema);
3170
+ break;
3171
+ }
3172
+ }
3173
+ return {
3174
+ type: "object",
3175
+ properties,
3176
+ additionalProperties: innerSchema.additionalProperties ?? true
3177
+ };
3178
+ }
3179
+ getInnerInputSchema() {
3180
+ if (!this.hasChildren())
3181
+ return;
3182
+ const tasks = this.subGraph.getTasks();
3183
+ if (tasks.length === 0)
3184
+ return;
3185
+ const startingNodes = tasks.filter((task) => this.subGraph.getSourceDataflows(task.config.id).length === 0);
3186
+ const sources = startingNodes.length > 0 ? startingNodes : tasks;
3187
+ const properties = {};
3188
+ const required = [];
3189
+ let additionalProperties = false;
3190
+ for (const task of sources) {
3191
+ const inputSchema = task.inputSchema();
3192
+ if (typeof inputSchema === "boolean") {
3193
+ if (inputSchema === true) {
3194
+ additionalProperties = true;
3195
+ }
3196
+ continue;
3197
+ }
3198
+ additionalProperties = additionalProperties || inputSchema.additionalProperties === true;
3199
+ for (const [key, prop] of Object.entries(inputSchema.properties || {})) {
3200
+ if (typeof prop === "boolean")
3201
+ continue;
3202
+ if (!properties[key]) {
3203
+ properties[key] = prop;
3204
+ }
3205
+ }
3206
+ for (const key of inputSchema.required || []) {
3207
+ if (!required.includes(key)) {
3208
+ required.push(key);
3209
+ }
3210
+ }
3211
+ }
3212
+ return {
3213
+ type: "object",
3214
+ properties,
3215
+ ...required.length > 0 ? { required } : {},
3216
+ additionalProperties
3217
+ };
3218
+ }
3219
+ getIterationInputSchema() {
3220
+ if (this._iterationInputSchema) {
3221
+ return this._iterationInputSchema;
3222
+ }
3223
+ this._iterationInputSchema = this.iterationInputConfig ? this.buildConfiguredIterationInputSchema() : this.buildDefaultIterationInputSchema();
3224
+ return this._iterationInputSchema;
3225
+ }
3226
+ setIterationInputSchema(schema) {
3227
+ this._iterationInputSchema = schema;
3228
+ this._inputSchemaNode = undefined;
3229
+ this.events.emit("regenerate");
3230
+ }
3231
+ setPropertyInputMode(propertyName, mode, baseSchema) {
3232
+ const currentSchema = this.getIterationInputSchema();
3233
+ if (typeof currentSchema === "boolean")
3234
+ return;
3235
+ const currentProps = currentSchema.properties || {};
3236
+ const existingProp = currentProps[propertyName];
3237
+ const base = baseSchema ?? (existingProp ? extractBaseSchema(existingProp) : { type: "string" });
3238
+ let newPropSchema;
3239
+ switch (mode) {
3240
+ case "array":
3241
+ newPropSchema = createArraySchema(base);
3242
+ break;
3243
+ case "scalar":
3244
+ newPropSchema = base;
3245
+ break;
3246
+ case "flexible":
3247
+ default:
3248
+ newPropSchema = createFlexibleSchema(base);
3249
+ break;
3250
+ }
3251
+ this._iterationInputSchema = {
3252
+ ...currentSchema,
3253
+ properties: {
3254
+ ...currentProps,
3255
+ [propertyName]: newPropSchema
3256
+ }
3257
+ };
3258
+ this._inputSchemaNode = undefined;
3259
+ this.events.emit("regenerate");
3260
+ }
3261
+ invalidateIterationInputSchema() {
3262
+ this._iterationInputSchema = undefined;
3263
+ this._iteratorPortInfo = undefined;
3264
+ this._inputSchemaNode = undefined;
3265
+ }
3266
+ analyzeIterationInput(input) {
3267
+ const inputData = input;
3268
+ const schema = this.hasChildren() ? this.getIterationInputSchema() : this.inputSchema();
3269
+ const schemaProps = typeof schema === "object" && schema.properties ? schema.properties : {};
3270
+ const keys = new Set([...Object.keys(schemaProps), ...Object.keys(inputData)]);
3271
+ const arrayPorts = [];
3272
+ const scalarPorts = [];
3273
+ const iteratedValues = {};
3274
+ const arrayLengths = [];
3275
+ for (const key of keys) {
3276
+ if (key.startsWith("_iteration"))
3277
+ continue;
3278
+ const value = inputData[key];
3279
+ const portSchema = schemaProps[key];
3280
+ let shouldIterate;
3281
+ const explicitFlag = getExplicitIterationFlag(portSchema);
3282
+ if (explicitFlag !== undefined) {
3283
+ shouldIterate = explicitFlag;
3284
+ } else {
3285
+ const schemaInference = inferIterationFromSchema(portSchema);
3286
+ shouldIterate = schemaInference ?? Array.isArray(value);
3287
+ }
3288
+ if (!shouldIterate) {
3289
+ scalarPorts.push(key);
3290
+ continue;
3291
+ }
3292
+ if (!Array.isArray(value)) {
3293
+ throw new TaskConfigurationError(`${this.type}: Input '${key}' is configured for iteration but value is not an array.`);
3294
+ }
3295
+ iteratedValues[key] = value;
3296
+ arrayPorts.push(key);
3297
+ arrayLengths.push(value.length);
3298
+ }
3299
+ if (arrayPorts.length === 0) {
3300
+ throw new TaskConfigurationError(`${this.type}: At least one array input is required for iteration. ` + `Mark a port with x-ui-iteration=true, provide array-typed schema, or pass array values at runtime.`);
3301
+ }
3302
+ const uniqueLengths = new Set(arrayLengths);
3303
+ if (uniqueLengths.size > 1) {
3304
+ const lengthInfo = arrayPorts.map((port, index) => `${port}=${arrayLengths[index]}`).join(", ");
3305
+ throw new TaskConfigurationError(`${this.type}: All iterated array inputs must have the same length (zip semantics). ` + `Found different lengths: ${lengthInfo}`);
3306
+ }
3307
+ const iterationCount = arrayLengths[0] ?? 0;
3308
+ const getIterationInput = (index) => {
3309
+ const iterInput = {};
3310
+ for (const key of arrayPorts) {
3311
+ iterInput[key] = iteratedValues[key][index];
3312
+ }
3313
+ for (const key of scalarPorts) {
3314
+ if (key in inputData) {
3315
+ iterInput[key] = inputData[key];
3316
+ }
3317
+ }
3318
+ return iterInput;
3319
+ };
3320
+ return {
3321
+ iterationCount,
3322
+ arrayPorts,
3323
+ scalarPorts,
3324
+ getIterationInput
3325
+ };
3326
+ }
3327
+ getIterationContextSchema() {
3328
+ return this.constructor.getIterationContextSchema();
3329
+ }
3330
+ inputSchema() {
3331
+ if (this.hasChildren()) {
3332
+ return this.getIterationInputSchema();
3333
+ }
3334
+ return this.constructor.inputSchema();
3335
+ }
3336
+ outputSchema() {
3337
+ if (!this.hasChildren()) {
3338
+ return this.constructor.outputSchema();
3339
+ }
3340
+ return this.getWrappedOutputSchema();
3341
+ }
3342
+ getWrappedOutputSchema() {
3343
+ if (!this.hasChildren()) {
3344
+ return { type: "object", properties: {}, additionalProperties: false };
3345
+ }
3346
+ const endingNodes = this.subGraph.getTasks().filter((task) => this.subGraph.getTargetDataflows(task.config.id).length === 0);
3347
+ if (endingNodes.length === 0) {
3348
+ return { type: "object", properties: {}, additionalProperties: false };
3349
+ }
3350
+ const properties = {};
3351
+ for (const task of endingNodes) {
3352
+ const taskOutputSchema = task.outputSchema();
3353
+ if (typeof taskOutputSchema === "boolean")
3354
+ continue;
3355
+ for (const [key, schema] of Object.entries(taskOutputSchema.properties || {})) {
3356
+ properties[key] = {
3357
+ type: "array",
3358
+ items: schema
3359
+ };
3360
+ }
3361
+ }
3362
+ return {
3363
+ type: "object",
3364
+ properties,
3365
+ additionalProperties: false
3366
+ };
3367
+ }
3368
+ }
3369
+
3370
+ // src/task/WhileTaskRunner.ts
3371
+ class WhileTaskRunner extends GraphAsTaskRunner {
3372
+ async executeTask(input) {
3373
+ const result = await this.task.execute(input, {
3374
+ signal: this.abortController.signal,
3375
+ updateProgress: this.handleProgress.bind(this),
3376
+ own: this.own,
3377
+ registry: this.registry
3378
+ });
3379
+ return result;
3380
+ }
3381
+ async executeTaskReactive(input, output) {
3382
+ const reactiveResult = await this.task.executeReactive(input, output, { own: this.own });
3383
+ return Object.assign({}, output, reactiveResult ?? {});
3384
+ }
3385
+ }
3386
+
3387
+ // src/task/WhileTask.ts
3388
+ var WHILE_CONTEXT_SCHEMA = {
3389
+ type: "object",
3390
+ properties: {
3391
+ _iterationIndex: {
3392
+ type: "integer",
3393
+ minimum: 0,
3394
+ title: "Iteration Number",
3395
+ description: "Current iteration number (0-based)",
3396
+ "x-ui-iteration": true
3397
+ }
3398
+ }
3399
+ };
3400
+
3401
+ class WhileTask extends GraphAsTask {
3402
+ static type = "WhileTask";
3403
+ static category = "Flow Control";
3404
+ static title = "While Loop";
3405
+ static description = "Loops until a condition function returns false";
3406
+ static hasDynamicSchemas = true;
3407
+ static getIterationContextSchema() {
3408
+ return WHILE_CONTEXT_SCHEMA;
3409
+ }
3410
+ _currentIteration = 0;
3411
+ constructor(input = {}, config = {}) {
3412
+ super(input, config);
3413
+ }
3414
+ get runner() {
3415
+ if (!this._runner) {
3416
+ this._runner = new WhileTaskRunner(this);
3417
+ }
3418
+ return this._runner;
3419
+ }
3420
+ get condition() {
3421
+ return this.config.condition;
3422
+ }
3423
+ get maxIterations() {
3424
+ if (this.config.maxIterations !== undefined)
3425
+ return this.config.maxIterations;
3426
+ const wc = this.config.extras?.whileConfig;
3427
+ return wc?.maxIterations ?? 100;
3428
+ }
3429
+ get chainIterations() {
3430
+ if (this.config.chainIterations !== undefined)
3431
+ return this.config.chainIterations;
3432
+ const wc = this.config.extras?.whileConfig;
3433
+ return wc?.chainIterations ?? true;
3434
+ }
3435
+ get currentIteration() {
3436
+ return this._currentIteration;
3437
+ }
3438
+ buildConditionFromExtras() {
3439
+ const wc = this.config.extras?.whileConfig;
3440
+ if (!wc?.conditionOperator) {
3441
+ return;
3442
+ }
3443
+ const { conditionField, conditionOperator, conditionValue } = wc;
3444
+ return (output) => {
3445
+ const fieldValue = conditionField ? getNestedValue(output, conditionField) : output;
3446
+ return evaluateCondition(fieldValue, conditionOperator, conditionValue ?? "");
3447
+ };
3448
+ }
3449
+ analyzeArrayInputs(input) {
3450
+ const wc = this.config.extras?.whileConfig;
3451
+ if (!wc?.iterationInputConfig) {
3452
+ return null;
3453
+ }
3454
+ const inputData = input;
3455
+ const config = wc.iterationInputConfig;
3456
+ const arrayPorts = [];
3457
+ const scalarPorts = [];
3458
+ const iteratedValues = {};
3459
+ const arrayLengths = [];
3460
+ for (const [key, propConfig] of Object.entries(config)) {
3461
+ const value = inputData[key];
3462
+ if (propConfig.mode === "array") {
3463
+ if (!Array.isArray(value)) {
3464
+ scalarPorts.push(key);
3465
+ continue;
3466
+ }
3467
+ iteratedValues[key] = value;
3468
+ arrayPorts.push(key);
3469
+ arrayLengths.push(value.length);
3470
+ } else {
3471
+ scalarPorts.push(key);
3472
+ }
3473
+ }
3474
+ for (const key of Object.keys(inputData)) {
3475
+ if (!config[key] && !key.startsWith("_iteration")) {
3476
+ scalarPorts.push(key);
3477
+ }
3478
+ }
3479
+ if (arrayPorts.length === 0) {
3480
+ return null;
3481
+ }
3482
+ const uniqueLengths = new Set(arrayLengths);
3483
+ if (uniqueLengths.size > 1) {
3484
+ const lengthInfo = arrayPorts.map((port, index) => `${port}=${arrayLengths[index]}`).join(", ");
3485
+ throw new TaskConfigurationError(`${this.type}: All iterated array inputs must have the same length. ` + `Found different lengths: ${lengthInfo}`);
3486
+ }
3487
+ return {
3488
+ arrayPorts,
3489
+ scalarPorts,
3490
+ iteratedValues,
3491
+ iterationCount: arrayLengths[0] ?? 0
3492
+ };
3493
+ }
3494
+ buildIterationInput(input, analysis, index) {
3495
+ const inputData = input;
3496
+ const iterInput = {};
3497
+ for (const key of analysis.arrayPorts) {
3498
+ iterInput[key] = analysis.iteratedValues[key][index];
3499
+ }
3500
+ for (const key of analysis.scalarPorts) {
3501
+ if (key in inputData) {
3502
+ iterInput[key] = inputData[key];
3503
+ }
3504
+ }
3505
+ return iterInput;
3506
+ }
3507
+ async execute(input, context) {
3508
+ if (!this.hasChildren()) {
3509
+ throw new TaskConfigurationError(`${this.type}: No subgraph set for while loop`);
3510
+ }
3511
+ const condition = this.condition ?? this.buildConditionFromExtras();
3512
+ if (!condition) {
3513
+ throw new TaskConfigurationError(`${this.type}: No condition function provided`);
3514
+ }
3515
+ const arrayAnalysis = this.analyzeArrayInputs(input);
3516
+ this._currentIteration = 0;
3517
+ let currentInput = { ...input };
3518
+ let currentOutput = {};
3519
+ const effectiveMax = arrayAnalysis ? Math.min(this.maxIterations, arrayAnalysis.iterationCount) : this.maxIterations;
3520
+ while (this._currentIteration < effectiveMax) {
3521
+ if (context.signal?.aborted) {
3522
+ break;
3523
+ }
3524
+ let iterationInput;
3525
+ if (arrayAnalysis) {
3526
+ iterationInput = {
3527
+ ...this.buildIterationInput(currentInput, arrayAnalysis, this._currentIteration),
3528
+ _iterationIndex: this._currentIteration
3529
+ };
3530
+ } else {
3531
+ iterationInput = {
3532
+ ...currentInput,
3533
+ _iterationIndex: this._currentIteration
3534
+ };
3535
+ }
3536
+ const results = await this.subGraph.run(iterationInput, {
3537
+ parentSignal: context.signal
3538
+ });
3539
+ currentOutput = this.subGraph.mergeExecuteOutputsToRunOutput(results, this.compoundMerge);
3540
+ if (!condition(currentOutput, this._currentIteration)) {
3541
+ break;
3542
+ }
3543
+ if (this.chainIterations) {
3544
+ currentInput = { ...currentInput, ...currentOutput };
3545
+ }
3546
+ this._currentIteration++;
3547
+ const progress = Math.min(this._currentIteration / effectiveMax * 100, 99);
3548
+ await context.updateProgress(progress, `Iteration ${this._currentIteration}`);
3549
+ }
3550
+ return currentOutput;
3551
+ }
3552
+ getIterationContextSchema() {
3553
+ return this.constructor.getIterationContextSchema();
3554
+ }
3555
+ getChainedOutputSchema() {
3556
+ if (!this.chainIterations)
3557
+ return;
3558
+ const outputSchema = this.outputSchema();
3559
+ if (typeof outputSchema === "boolean")
3560
+ return;
3561
+ const properties = {};
3562
+ if (outputSchema.properties && typeof outputSchema.properties === "object") {
3563
+ for (const [key, schema] of Object.entries(outputSchema.properties)) {
3564
+ if (key === "_iterations")
3565
+ continue;
3566
+ if (typeof schema === "object" && schema !== null) {
3567
+ properties[key] = { ...schema, "x-ui-iteration": true };
3568
+ }
3569
+ }
3570
+ }
3571
+ if (Object.keys(properties).length === 0)
3572
+ return;
3573
+ return { type: "object", properties };
3574
+ }
3575
+ inputSchema() {
3576
+ if (!this.hasChildren()) {
3577
+ return this.constructor.inputSchema();
3578
+ }
3579
+ const baseSchema = super.inputSchema();
3580
+ if (typeof baseSchema === "boolean")
3581
+ return baseSchema;
3582
+ const wc = this.config.extras?.whileConfig;
3583
+ if (!wc?.iterationInputConfig) {
3584
+ return baseSchema;
3585
+ }
3586
+ const properties = { ...baseSchema.properties || {} };
3587
+ for (const [key, propConfig] of Object.entries(wc.iterationInputConfig)) {
3588
+ if (propConfig.mode === "array" && properties[key]) {
3589
+ const scalarSchema = properties[key];
3590
+ properties[key] = {
3591
+ anyOf: [scalarSchema, { type: "array", items: scalarSchema }]
3592
+ };
3593
+ }
3594
+ }
3595
+ return {
3596
+ ...baseSchema,
3597
+ properties
3598
+ };
3599
+ }
3600
+ static inputSchema() {
3601
+ return {
3602
+ type: "object",
3603
+ properties: {},
3604
+ additionalProperties: true
3605
+ };
3606
+ }
3607
+ static outputSchema() {
3608
+ return {
3609
+ type: "object",
3610
+ properties: {
3611
+ _iterations: {
3612
+ type: "number",
3613
+ title: "Iterations",
3614
+ description: "Number of iterations executed"
3615
+ }
3616
+ },
3617
+ additionalProperties: true
3618
+ };
3619
+ }
3620
+ outputSchema() {
3621
+ if (!this.hasChildren()) {
3622
+ return this.constructor.outputSchema();
3623
+ }
3624
+ const tasks = this.subGraph.getTasks();
3625
+ const endingNodes = tasks.filter((task) => this.subGraph.getTargetDataflows(task.config.id).length === 0);
3626
+ if (endingNodes.length === 0) {
3627
+ return this.constructor.outputSchema();
3628
+ }
3629
+ const properties = {
3630
+ _iterations: {
3631
+ type: "number",
3632
+ title: "Iterations",
3633
+ description: "Number of iterations executed"
3634
+ }
3635
+ };
3636
+ for (const task of endingNodes) {
3637
+ const taskOutputSchema = task.outputSchema();
3638
+ if (typeof taskOutputSchema === "boolean")
3639
+ continue;
3640
+ const taskProperties = taskOutputSchema.properties || {};
3641
+ for (const [key, schema] of Object.entries(taskProperties)) {
3642
+ if (!properties[key]) {
3643
+ properties[key] = schema;
3644
+ }
3645
+ }
3646
+ }
3647
+ return {
3648
+ type: "object",
3649
+ properties,
3650
+ additionalProperties: false
3651
+ };
3652
+ }
3653
+ }
3654
+ Workflow.prototype.while = CreateLoopWorkflow(WhileTask);
3655
+ Workflow.prototype.endWhile = CreateEndLoopWorkflow("endWhile");
3656
+
3657
+ // src/task/iterationSchema.ts
3658
+ function isFlexibleSchema(schema) {
3659
+ if (typeof schema === "boolean")
3660
+ return false;
3661
+ const variants = schema.oneOf ?? schema.anyOf;
3662
+ const arr = Array.isArray(variants) ? variants : undefined;
3663
+ if (!arr || arr.length !== 2)
3664
+ return false;
3665
+ let hasScalar = false;
3666
+ let hasArray = false;
3667
+ for (const variant of arr) {
3668
+ if (typeof variant !== "object")
3669
+ continue;
3670
+ const v = variant;
3671
+ if (v.type === "array" || "items" in v) {
3672
+ hasArray = true;
3673
+ } else {
3674
+ hasScalar = true;
3675
+ }
3676
+ }
3677
+ return hasScalar && hasArray;
3678
+ }
3679
+ function isStrictArraySchema(schema) {
3680
+ if (typeof schema === "boolean")
3681
+ return false;
3682
+ const s = schema;
3683
+ return s.type === "array" && !isFlexibleSchema(schema);
3684
+ }
3685
+ function getInputModeFromSchema(schema) {
3686
+ if (isFlexibleSchema(schema))
3687
+ return "flexible";
3688
+ if (isStrictArraySchema(schema))
3689
+ return "array";
3690
+ return "scalar";
3691
+ }
3692
+ function getIterationContextSchemaForType(taskType) {
3693
+ if (taskType === "MapTask" || taskType === "ReduceTask") {
3694
+ return ITERATOR_CONTEXT_SCHEMA;
3695
+ }
3696
+ if (taskType === "WhileTask") {
3697
+ return WHILE_CONTEXT_SCHEMA;
3698
+ }
3699
+ return;
3700
+ }
3701
+ function addIterationContextToSchema(existingSchema, parentTaskType) {
3702
+ const contextSchema = getIterationContextSchemaForType(parentTaskType);
3703
+ if (!contextSchema) {
3704
+ return existingSchema ?? { type: "object", properties: {} };
3705
+ }
3706
+ const baseProperties = existingSchema && typeof existingSchema !== "boolean" && existingSchema.properties && typeof existingSchema.properties !== "boolean" ? existingSchema.properties : {};
3707
+ const contextProperties = typeof contextSchema !== "boolean" && contextSchema.properties && typeof contextSchema.properties !== "boolean" ? contextSchema.properties : {};
3708
+ return {
3709
+ type: "object",
3710
+ properties: {
3711
+ ...baseProperties,
3712
+ ...contextProperties
3713
+ }
3714
+ };
3715
+ }
3716
+ function isIterationProperty(schema) {
3717
+ if (!schema || typeof schema === "boolean")
3718
+ return false;
3719
+ return schema["x-ui-iteration"] === true;
3720
+ }
3721
+ function filterIterationProperties(schema) {
3722
+ if (!schema || typeof schema === "boolean")
3723
+ return schema;
3724
+ const props = schema.properties;
3725
+ if (!props || typeof props === "boolean")
3726
+ return schema;
3727
+ const filteredProps = {};
3728
+ for (const [key, propSchema] of Object.entries(props)) {
3729
+ if (!isIterationProperty(propSchema)) {
3730
+ filteredProps[key] = propSchema;
3731
+ }
3732
+ }
3733
+ if (Object.keys(filteredProps).length === 0) {
3734
+ return { type: "object", properties: {} };
3735
+ }
3736
+ return { ...schema, properties: filteredProps };
3737
+ }
3738
+ function extractIterationProperties(schema) {
3739
+ if (!schema || typeof schema === "boolean")
3740
+ return;
3741
+ const props = schema.properties;
3742
+ if (!props || typeof props === "boolean")
3743
+ return;
3744
+ const iterProps = {};
3745
+ for (const [key, propSchema] of Object.entries(props)) {
3746
+ if (isIterationProperty(propSchema)) {
3747
+ iterProps[key] = propSchema;
3748
+ }
3749
+ }
3750
+ if (Object.keys(iterProps).length === 0)
3751
+ return;
3752
+ return { type: "object", properties: iterProps };
3753
+ }
3754
+ function removeIterationProperties(schema) {
3755
+ return filterIterationProperties(schema);
3756
+ }
3757
+ function mergeChainedOutputToInput(inputSchema, outputSchema) {
3758
+ const baseSchema = filterIterationProperties(inputSchema) ?? {
3759
+ type: "object",
3760
+ properties: {}
3761
+ };
3762
+ if (!outputSchema || typeof outputSchema === "boolean") {
3763
+ return baseSchema;
3764
+ }
3765
+ const outProps = outputSchema.properties;
3766
+ if (!outProps || typeof outProps === "boolean") {
3767
+ return baseSchema;
3768
+ }
3769
+ const baseProps = typeof baseSchema !== "boolean" && baseSchema.properties && typeof baseSchema.properties !== "boolean" ? baseSchema.properties : {};
3770
+ const mergedProperties = { ...baseProps };
3771
+ for (const [key, propSchema] of Object.entries(outProps)) {
3772
+ if (typeof propSchema === "object" && propSchema !== null) {
3773
+ mergedProperties[key] = { ...propSchema, "x-ui-iteration": true };
3774
+ }
3775
+ }
3776
+ return {
3777
+ type: "object",
3778
+ properties: mergedProperties
3779
+ };
3780
+ }
3781
+ function buildIterationInputSchema(innerSchema, config) {
3782
+ if (!innerSchema || typeof innerSchema === "boolean") {
3783
+ return { type: "object", properties: {} };
3784
+ }
3785
+ const innerProps = innerSchema.properties;
3786
+ if (!innerProps || typeof innerProps === "boolean") {
3787
+ return { type: "object", properties: {} };
3788
+ }
3789
+ const properties = {};
3790
+ const propsRecord = innerProps;
3791
+ for (const [key, propSchema] of Object.entries(propsRecord)) {
3792
+ if (typeof propSchema === "boolean")
3793
+ continue;
3794
+ if (propSchema["x-ui-iteration"]) {
3795
+ continue;
3796
+ }
3797
+ const originalProps = propSchema;
3798
+ const metadata = {};
3799
+ for (const metaKey of Object.keys(originalProps)) {
3800
+ if (metaKey === "title" || metaKey === "description" || metaKey.startsWith("x-")) {
3801
+ metadata[metaKey] = originalProps[metaKey];
3802
+ }
3803
+ }
3804
+ const baseSchema = extractBaseSchema(propSchema);
3805
+ const propConfig = config?.[key];
3806
+ const mode = propConfig?.mode ?? "flexible";
3807
+ const base = propConfig?.baseSchema ?? baseSchema;
3808
+ let wrappedSchema;
3809
+ switch (mode) {
3810
+ case "array":
3811
+ wrappedSchema = createArraySchema(base);
3812
+ break;
3813
+ case "scalar":
3814
+ wrappedSchema = base;
3815
+ break;
3816
+ case "flexible":
3817
+ default:
3818
+ wrappedSchema = createFlexibleSchema(base);
3819
+ break;
3820
+ }
3821
+ if (Object.keys(metadata).length > 0 && typeof wrappedSchema === "object") {
3822
+ properties[key] = { ...metadata, ...wrappedSchema };
3823
+ } else {
3824
+ properties[key] = wrappedSchema;
3825
+ }
3826
+ }
3827
+ return {
3828
+ type: "object",
3829
+ properties
3830
+ };
3831
+ }
3832
+ function findArrayPorts(schema) {
3833
+ if (!schema || typeof schema === "boolean")
3834
+ return [];
3835
+ const props = schema.properties;
3836
+ if (!props || typeof props === "boolean")
3837
+ return [];
3838
+ const arrayPorts = [];
3839
+ const propsRecord = props;
3840
+ for (const [key, propSchema] of Object.entries(propsRecord)) {
3841
+ if (typeof propSchema === "boolean")
3842
+ continue;
3843
+ if (propSchema.type === "array") {
3844
+ arrayPorts.push(key);
3845
+ }
3846
+ }
3847
+ return arrayPorts;
3848
+ }
3849
+ function wrapSchemaInArray(schema) {
3850
+ if (!schema || typeof schema === "boolean")
3851
+ return schema;
3852
+ const props = schema.properties;
3853
+ if (!props || typeof props === "boolean")
3854
+ return schema;
3855
+ const propsRecord = props;
3856
+ const wrappedProperties = {};
3857
+ for (const [key, propSchema] of Object.entries(propsRecord)) {
3858
+ wrappedProperties[key] = {
3859
+ type: "array",
3860
+ items: propSchema
3861
+ };
3862
+ }
3863
+ return {
3864
+ type: "object",
3865
+ properties: wrappedProperties
3866
+ };
3867
+ }
3868
+ // src/task/JobQueueFactory.ts
3869
+ import {
3870
+ JobQueueClient,
3871
+ JobQueueServer
3872
+ } from "@workglow/job-queue";
3873
+ import { InMemoryQueueStorage } from "@workglow/storage";
3874
+ import { createServiceToken as createServiceToken2, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
3875
+ var JOB_QUEUE_FACTORY = createServiceToken2("taskgraph.jobQueueFactory");
3876
+ var defaultJobQueueFactory = async ({
3877
+ queueName,
3878
+ jobClass,
3879
+ options
3880
+ }) => {
3881
+ const storage = options?.storage ?? new InMemoryQueueStorage(queueName);
3882
+ await storage.setupDatabase();
3883
+ const server = new JobQueueServer(jobClass, {
3884
+ storage,
3885
+ queueName,
3886
+ limiter: options?.limiter,
3887
+ workerCount: options?.workerCount,
3888
+ pollIntervalMs: options?.pollIntervalMs,
3889
+ deleteAfterCompletionMs: options?.deleteAfterCompletionMs,
3890
+ deleteAfterFailureMs: options?.deleteAfterFailureMs,
3891
+ deleteAfterDisabledMs: options?.deleteAfterDisabledMs,
3892
+ cleanupIntervalMs: options?.cleanupIntervalMs
3893
+ });
3894
+ const client = new JobQueueClient({
3895
+ storage,
3896
+ queueName
3897
+ });
3898
+ client.attach(server);
3899
+ return { server, client, storage };
3900
+ };
3901
+ function registerJobQueueFactory(factory) {
3902
+ globalServiceRegistry3.registerInstance(JOB_QUEUE_FACTORY, factory);
3903
+ }
3904
+ function createJobQueueFactoryWithOptions(defaultOptions = {}) {
3905
+ return async ({
3906
+ queueName,
3907
+ jobClass,
3908
+ options
3909
+ }) => {
3910
+ const mergedOptions = {
3911
+ ...defaultOptions,
3912
+ ...options ?? {}
3913
+ };
3914
+ const storage = mergedOptions.storage ?? new InMemoryQueueStorage(queueName);
3915
+ await storage.setupDatabase();
3916
+ const server = new JobQueueServer(jobClass, {
3917
+ storage,
3918
+ queueName,
3919
+ limiter: mergedOptions.limiter,
3920
+ workerCount: mergedOptions.workerCount,
3921
+ pollIntervalMs: mergedOptions.pollIntervalMs,
3922
+ deleteAfterCompletionMs: mergedOptions.deleteAfterCompletionMs,
3923
+ deleteAfterFailureMs: mergedOptions.deleteAfterFailureMs,
3924
+ deleteAfterDisabledMs: mergedOptions.deleteAfterDisabledMs,
3925
+ cleanupIntervalMs: mergedOptions.cleanupIntervalMs
3926
+ });
3927
+ const client = new JobQueueClient({
3928
+ storage,
3929
+ queueName
3930
+ });
3931
+ client.attach(server);
3932
+ return { server, client, storage };
3933
+ };
3934
+ }
3935
+ function getJobQueueFactory() {
3936
+ if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
3937
+ registerJobQueueFactory(defaultJobQueueFactory);
3938
+ }
3939
+ return globalServiceRegistry3.get(JOB_QUEUE_FACTORY);
3940
+ }
3941
+ if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
2597
3942
  registerJobQueueFactory(defaultJobQueueFactory);
2598
3943
  }
2599
3944
  // src/task/JobQueueTask.ts
@@ -2780,6 +4125,155 @@ class JobQueueTask extends GraphAsTask {
2780
4125
  super.abort();
2781
4126
  }
2782
4127
  }
4128
+ // src/task/MapTask.ts
4129
+ class MapTask extends IteratorTask {
4130
+ static type = "MapTask";
4131
+ static category = "Flow Control";
4132
+ static title = "Map";
4133
+ static description = "Transforms array inputs by running a workflow per item";
4134
+ static compoundMerge = PROPERTY_ARRAY;
4135
+ static inputSchema() {
4136
+ return {
4137
+ type: "object",
4138
+ properties: {},
4139
+ additionalProperties: true
4140
+ };
4141
+ }
4142
+ static outputSchema() {
4143
+ return {
4144
+ type: "object",
4145
+ properties: {},
4146
+ additionalProperties: true
4147
+ };
4148
+ }
4149
+ get preserveOrder() {
4150
+ return this.config.preserveOrder ?? true;
4151
+ }
4152
+ get flatten() {
4153
+ return this.config.flatten ?? false;
4154
+ }
4155
+ preserveIterationOrder() {
4156
+ return this.preserveOrder;
4157
+ }
4158
+ getEmptyResult() {
4159
+ const schema = this.outputSchema();
4160
+ if (typeof schema === "boolean") {
4161
+ return {};
4162
+ }
4163
+ const result = {};
4164
+ for (const key of Object.keys(schema.properties || {})) {
4165
+ result[key] = [];
4166
+ }
4167
+ return result;
4168
+ }
4169
+ outputSchema() {
4170
+ if (!this.hasChildren()) {
4171
+ return this.constructor.outputSchema();
4172
+ }
4173
+ return this.getWrappedOutputSchema();
4174
+ }
4175
+ collectResults(results) {
4176
+ const collected = super.collectResults(results);
4177
+ if (!this.flatten || typeof collected !== "object" || collected === null) {
4178
+ return collected;
4179
+ }
4180
+ const flattened = {};
4181
+ for (const [key, value] of Object.entries(collected)) {
4182
+ if (Array.isArray(value)) {
4183
+ flattened[key] = value.flat();
4184
+ } else {
4185
+ flattened[key] = value;
4186
+ }
4187
+ }
4188
+ return flattened;
4189
+ }
4190
+ }
4191
+ queueMicrotask(() => {
4192
+ Workflow.prototype.map = CreateLoopWorkflow(MapTask);
4193
+ Workflow.prototype.endMap = CreateEndLoopWorkflow("endMap");
4194
+ });
4195
+ // src/task/ReduceTask.ts
4196
+ class ReduceTask extends IteratorTask {
4197
+ static type = "ReduceTask";
4198
+ static category = "Flow Control";
4199
+ static title = "Reduce";
4200
+ static description = "Processes iterated inputs sequentially with an accumulator (fold)";
4201
+ constructor(input = {}, config = {}) {
4202
+ const reduceConfig = {
4203
+ ...config,
4204
+ concurrencyLimit: 1,
4205
+ batchSize: 1
4206
+ };
4207
+ super(input, reduceConfig);
4208
+ }
4209
+ get initialValue() {
4210
+ return this.config.initialValue ?? {};
4211
+ }
4212
+ isReduceTask() {
4213
+ return true;
4214
+ }
4215
+ getInitialAccumulator() {
4216
+ const value = this.initialValue;
4217
+ if (Array.isArray(value)) {
4218
+ return [...value];
4219
+ }
4220
+ if (value && typeof value === "object") {
4221
+ return { ...value };
4222
+ }
4223
+ return value;
4224
+ }
4225
+ buildIterationRunInput(analysis, index, iterationCount, extraInput = {}) {
4226
+ return super.buildIterationRunInput(analysis, index, iterationCount, {
4227
+ accumulator: extraInput.accumulator
4228
+ });
4229
+ }
4230
+ getEmptyResult() {
4231
+ return this.getInitialAccumulator();
4232
+ }
4233
+ static inputSchema() {
4234
+ return {
4235
+ type: "object",
4236
+ properties: {},
4237
+ additionalProperties: true
4238
+ };
4239
+ }
4240
+ static outputSchema() {
4241
+ return {
4242
+ type: "object",
4243
+ properties: {},
4244
+ additionalProperties: true
4245
+ };
4246
+ }
4247
+ outputSchema() {
4248
+ if (!this.hasChildren()) {
4249
+ return this.constructor.outputSchema();
4250
+ }
4251
+ const endingNodes = this.subGraph.getTasks().filter((task) => this.subGraph.getTargetDataflows(task.config.id).length === 0);
4252
+ if (endingNodes.length === 0) {
4253
+ return this.constructor.outputSchema();
4254
+ }
4255
+ const properties = {};
4256
+ for (const task of endingNodes) {
4257
+ const taskOutputSchema = task.outputSchema();
4258
+ if (typeof taskOutputSchema === "boolean")
4259
+ continue;
4260
+ for (const [key, schema] of Object.entries(taskOutputSchema.properties || {})) {
4261
+ if (!properties[key]) {
4262
+ properties[key] = schema;
4263
+ }
4264
+ }
4265
+ }
4266
+ return {
4267
+ type: "object",
4268
+ properties,
4269
+ additionalProperties: false
4270
+ };
4271
+ }
4272
+ }
4273
+ queueMicrotask(() => {
4274
+ Workflow.prototype.reduce = CreateLoopWorkflow(ReduceTask);
4275
+ Workflow.prototype.endReduce = CreateEndLoopWorkflow("endReduce");
4276
+ });
2783
4277
  // src/task/TaskRegistry.ts
2784
4278
  var taskConstructors = new Map;
2785
4279
  function registerTask(baseClass) {
@@ -2849,7 +4343,7 @@ var createGraphFromGraphJSON = (graphJsonObj) => {
2849
4343
  };
2850
4344
  // src/task/index.ts
2851
4345
  var registerBaseTasks = () => {
2852
- const tasks = [ConditionalTask, GraphAsTask];
4346
+ const tasks = [GraphAsTask, ConditionalTask, MapTask, WhileTask, ReduceTask];
2853
4347
  tasks.map(TaskRegistry.registerTask);
2854
4348
  return tasks;
2855
4349
  };
@@ -3010,25 +4504,47 @@ class TaskOutputTabularRepository extends TaskOutputRepository {
3010
4504
  }
3011
4505
  }
3012
4506
  export {
4507
+ wrapSchemaInArray,
3013
4508
  setTaskQueueRegistry,
3014
4509
  serialGraph,
4510
+ schemaAcceptsArray,
3015
4511
  resolveSchemaInputs,
4512
+ removeIterationProperties,
3016
4513
  registerJobQueueFactory,
3017
4514
  registerBaseTasks,
3018
4515
  pipe,
3019
4516
  parallel,
4517
+ mergeChainedOutputToInput,
4518
+ isStrictArraySchema,
4519
+ isIterationProperty,
4520
+ isFlexibleSchema,
3020
4521
  getTaskQueueRegistry,
4522
+ getNestedValue,
3021
4523
  getLastTask,
3022
4524
  getJobQueueFactory,
4525
+ getIterationContextSchemaForType,
4526
+ getInputModeFromSchema,
4527
+ findArrayPorts,
4528
+ filterIterationProperties,
4529
+ extractIterationProperties,
4530
+ extractBaseSchema,
4531
+ evaluateCondition,
3023
4532
  ensureTask,
3024
4533
  createTaskFromGraphJSON,
3025
4534
  createTaskFromDependencyJSON,
3026
4535
  createJobQueueFactoryWithOptions,
3027
4536
  createGraphFromGraphJSON,
3028
4537
  createGraphFromDependencyJSON,
4538
+ createFlexibleSchema,
4539
+ createArraySchema,
3029
4540
  connect,
4541
+ buildIterationInputSchema,
4542
+ addIterationContextToSchema,
3030
4543
  WorkflowError,
3031
4544
  Workflow,
4545
+ WhileTaskRunner,
4546
+ WhileTask,
4547
+ WHILE_CONTEXT_SCHEMA,
3032
4548
  TaskStatus,
3033
4549
  TaskRegistry,
3034
4550
  TaskQueueRegistry,
@@ -3051,10 +4567,15 @@ export {
3051
4567
  Task,
3052
4568
  TASK_OUTPUT_REPOSITORY,
3053
4569
  TASK_GRAPH_REPOSITORY,
4570
+ ReduceTask,
3054
4571
  PROPERTY_ARRAY,
4572
+ MapTask,
3055
4573
  JobTaskFailedError,
3056
4574
  JobQueueTask,
3057
4575
  JOB_QUEUE_FACTORY,
4576
+ IteratorTaskRunner,
4577
+ IteratorTask,
4578
+ ITERATOR_CONTEXT_SCHEMA,
3058
4579
  GraphAsTaskRunner,
3059
4580
  GraphAsTask,
3060
4581
  GRAPH_RESULT_ARRAY,
@@ -3065,7 +4586,9 @@ export {
3065
4586
  DATAFLOW_ERROR_PORT,
3066
4587
  DATAFLOW_ALL_PORTS,
3067
4588
  CreateWorkflow,
4589
+ CreateLoopWorkflow,
4590
+ CreateEndLoopWorkflow,
3068
4591
  ConditionalTask
3069
4592
  };
3070
4593
 
3071
- //# debugId=0C9B0184C88D909D64756E2164756E21
4594
+ //# debugId=4D22DE67A27A853E64756E2164756E21