@workglow/task-graph 0.2.13 → 0.2.15

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 (89) hide show
  1. package/dist/__tests__/public-exports.test.d.ts +7 -0
  2. package/dist/__tests__/public-exports.test.d.ts.map +1 -0
  3. package/dist/browser.js +1068 -404
  4. package/dist/browser.js.map +30 -20
  5. package/dist/bun.js +1063 -399
  6. package/dist/bun.js.map +30 -20
  7. package/dist/common.d.ts +4 -0
  8. package/dist/common.d.ts.map +1 -1
  9. package/dist/node.js +1063 -399
  10. package/dist/node.js.map +30 -20
  11. package/dist/task/EntitlementEnforcer.d.ts +37 -4
  12. package/dist/task/EntitlementEnforcer.d.ts.map +1 -1
  13. package/dist/task/EntitlementPolicy.d.ts +10 -0
  14. package/dist/task/EntitlementPolicy.d.ts.map +1 -1
  15. package/dist/task/FallbackTask.d.ts +2 -1
  16. package/dist/task/FallbackTask.d.ts.map +1 -1
  17. package/dist/task/GraphAsTask.d.ts +8 -0
  18. package/dist/task/GraphAsTask.d.ts.map +1 -1
  19. package/dist/task/IteratorTask.d.ts +26 -7
  20. package/dist/task/IteratorTask.d.ts.map +1 -1
  21. package/dist/task/IteratorTaskRunner.d.ts +1 -1
  22. package/dist/task/IteratorTaskRunner.d.ts.map +1 -1
  23. package/dist/task/MapTask.d.ts +47 -3
  24. package/dist/task/MapTask.d.ts.map +1 -1
  25. package/dist/task/ReduceTask.d.ts +10 -3
  26. package/dist/task/ReduceTask.d.ts.map +1 -1
  27. package/dist/task/Task.d.ts +4 -1
  28. package/dist/task/Task.d.ts.map +1 -1
  29. package/dist/task/TaskJSON.d.ts +10 -1
  30. package/dist/task/TaskJSON.d.ts.map +1 -1
  31. package/dist/task/WhileTask.d.ts +20 -7
  32. package/dist/task/WhileTask.d.ts.map +1 -1
  33. package/dist/task/__tests__/DataflowJson.transforms.test.d.ts +7 -0
  34. package/dist/task/__tests__/DataflowJson.transforms.test.d.ts.map +1 -0
  35. package/dist/task-graph/ConditionalBuilder.d.ts +49 -0
  36. package/dist/task-graph/ConditionalBuilder.d.ts.map +1 -0
  37. package/dist/task-graph/Conversions.d.ts.map +1 -1
  38. package/dist/task-graph/Dataflow.d.ts +41 -1
  39. package/dist/task-graph/Dataflow.d.ts.map +1 -1
  40. package/dist/task-graph/GraphSchemaUtils.d.ts +2 -2
  41. package/dist/task-graph/GraphSchemaUtils.d.ts.map +1 -1
  42. package/dist/task-graph/GraphToWorkflowCode.d.ts.map +1 -1
  43. package/dist/task-graph/TaskGraph.d.ts +6 -0
  44. package/dist/task-graph/TaskGraph.d.ts.map +1 -1
  45. package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
  46. package/dist/task-graph/TransformRegistry.d.ts +14 -0
  47. package/dist/task-graph/TransformRegistry.d.ts.map +1 -0
  48. package/dist/task-graph/TransformTypes.d.ts +51 -0
  49. package/dist/task-graph/TransformTypes.d.ts.map +1 -0
  50. package/dist/task-graph/Workflow.d.ts +31 -3
  51. package/dist/task-graph/Workflow.d.ts.map +1 -1
  52. package/dist/task-graph/__tests__/Dataflow.streaming.test.d.ts +7 -0
  53. package/dist/task-graph/__tests__/Dataflow.streaming.test.d.ts.map +1 -0
  54. package/dist/task-graph/__tests__/Dataflow.transforms.test.d.ts +7 -0
  55. package/dist/task-graph/__tests__/Dataflow.transforms.test.d.ts.map +1 -0
  56. package/dist/task-graph/__tests__/TaskGraphRunner.transforms.test.d.ts +7 -0
  57. package/dist/task-graph/__tests__/TaskGraphRunner.transforms.test.d.ts.map +1 -0
  58. package/dist/task-graph/__tests__/TransformRegistry.test.d.ts +6 -0
  59. package/dist/task-graph/__tests__/TransformRegistry.test.d.ts.map +1 -0
  60. package/dist/task-graph/__tests__/transforms/coalesce.test.d.ts +6 -0
  61. package/dist/task-graph/__tests__/transforms/coalesce.test.d.ts.map +1 -0
  62. package/dist/task-graph/__tests__/transforms/date-conversions.test.d.ts +6 -0
  63. package/dist/task-graph/__tests__/transforms/date-conversions.test.d.ts.map +1 -0
  64. package/dist/task-graph/__tests__/transforms/index-access.test.d.ts +6 -0
  65. package/dist/task-graph/__tests__/transforms/index-access.test.d.ts.map +1 -0
  66. package/dist/task-graph/__tests__/transforms/pick.test.d.ts +6 -0
  67. package/dist/task-graph/__tests__/transforms/pick.test.d.ts.map +1 -0
  68. package/dist/task-graph/__tests__/transforms/scalar-conversions.test.d.ts +6 -0
  69. package/dist/task-graph/__tests__/transforms/scalar-conversions.test.d.ts.map +1 -0
  70. package/dist/task-graph/__tests__/transforms/string-casts.test.d.ts +6 -0
  71. package/dist/task-graph/__tests__/transforms/string-casts.test.d.ts.map +1 -0
  72. package/dist/task-graph/autoConnect.d.ts +39 -0
  73. package/dist/task-graph/autoConnect.d.ts.map +1 -0
  74. package/dist/task-graph/transforms/coalesce.d.ts +11 -0
  75. package/dist/task-graph/transforms/coalesce.d.ts.map +1 -0
  76. package/dist/task-graph/transforms/date-conversions.d.ts +12 -0
  77. package/dist/task-graph/transforms/date-conversions.d.ts.map +1 -0
  78. package/dist/task-graph/transforms/index-access.d.ts +11 -0
  79. package/dist/task-graph/transforms/index-access.d.ts.map +1 -0
  80. package/dist/task-graph/transforms/index.d.ts +18 -0
  81. package/dist/task-graph/transforms/index.d.ts.map +1 -0
  82. package/dist/task-graph/transforms/pick.d.ts +11 -0
  83. package/dist/task-graph/transforms/pick.d.ts.map +1 -0
  84. package/dist/task-graph/transforms/scalar-conversions.d.ts +10 -0
  85. package/dist/task-graph/transforms/scalar-conversions.d.ts.map +1 -0
  86. package/dist/task-graph/transforms/string-casts.d.ts +18 -0
  87. package/dist/task-graph/transforms/string-casts.d.ts.map +1 -0
  88. package/package.json +7 -7
  89. package/src/EXECUTION_MODEL.md +6 -0
package/dist/browser.js CHANGED
@@ -2,6 +2,98 @@
2
2
  import { areSemanticallyCompatible } from "@workglow/util/schema";
3
3
  import { EventEmitter } from "@workglow/util";
4
4
 
5
+ // src/task/TaskError.ts
6
+ import { BaseError } from "@workglow/util";
7
+
8
+ class TaskError extends BaseError {
9
+ static type = "TaskError";
10
+ taskType;
11
+ taskId;
12
+ constructor(message) {
13
+ super(message);
14
+ }
15
+ }
16
+
17
+ class TaskConfigurationError extends TaskError {
18
+ static type = "TaskConfigurationError";
19
+ constructor(message) {
20
+ super(message);
21
+ }
22
+ }
23
+
24
+ class WorkflowError extends TaskError {
25
+ static type = "WorkflowError";
26
+ constructor(message) {
27
+ super(message);
28
+ }
29
+ }
30
+
31
+ class TaskAbortedError extends TaskError {
32
+ static type = "TaskAbortedError";
33
+ constructor(message = "Task aborted") {
34
+ super(message);
35
+ }
36
+ }
37
+
38
+ class TaskTimeoutError extends TaskAbortedError {
39
+ static type = "TaskTimeoutError";
40
+ constructor(timeoutMs) {
41
+ super(timeoutMs ? `Task timed out after ${timeoutMs}ms` : "Task timed out");
42
+ }
43
+ }
44
+
45
+ class TaskGraphTimeoutError extends TaskTimeoutError {
46
+ static type = "TaskGraphTimeoutError";
47
+ constructor(timeoutMs) {
48
+ super(timeoutMs);
49
+ this.message = timeoutMs ? `Graph execution timed out after ${timeoutMs}ms` : "Graph execution timed out";
50
+ }
51
+ }
52
+
53
+ class TaskFailedError extends TaskError {
54
+ static type = "TaskFailedError";
55
+ constructor(message = "Task failed") {
56
+ super(message);
57
+ }
58
+ }
59
+
60
+ class JobTaskFailedError extends TaskFailedError {
61
+ static type = "JobTaskFailedError";
62
+ jobError;
63
+ constructor(err) {
64
+ super(String(err));
65
+ this.jobError = err;
66
+ }
67
+ }
68
+
69
+ class TaskJSONError extends TaskError {
70
+ static type = "TaskJSONError";
71
+ constructor(message = "Error converting JSON to a Task") {
72
+ super(message);
73
+ }
74
+ }
75
+
76
+ class TaskInvalidInputError extends TaskError {
77
+ static type = "TaskInvalidInputError";
78
+ constructor(message = "Invalid input data") {
79
+ super(message);
80
+ }
81
+ }
82
+
83
+ class TaskEntitlementError extends TaskError {
84
+ static type = "TaskEntitlementError";
85
+ constructor(message = "Required entitlements denied") {
86
+ super(message);
87
+ }
88
+ }
89
+
90
+ class TaskSerializationError extends TaskError {
91
+ static type = "TaskSerializationError";
92
+ constructor(taskType) {
93
+ super(`Task "${taskType}" cannot be serialized: config contains non-serializable values. ` + `Use a declarative config alternative or remove function-valued config properties.`);
94
+ }
95
+ }
96
+
5
97
  // src/task/TaskTypes.ts
6
98
  var TaskStatus = {
7
99
  PENDING: "PENDING",
@@ -48,6 +140,21 @@ var TaskConfigSchema = {
48
140
  additionalProperties: false
49
141
  };
50
142
 
143
+ // src/task-graph/TransformRegistry.ts
144
+ import { createServiceToken, globalServiceRegistry } from "@workglow/util";
145
+ var transformDefs = new Map;
146
+ var TransformRegistry = {
147
+ all: transformDefs,
148
+ registerTransform(def) {
149
+ transformDefs.set(def.id, def);
150
+ },
151
+ unregisterTransform(id) {
152
+ transformDefs.delete(id);
153
+ }
154
+ };
155
+ var TRANSFORM_DEFS = createServiceToken("transform.defs");
156
+ globalServiceRegistry.registerIfAbsent(TRANSFORM_DEFS, () => TransformRegistry.all, true);
157
+
51
158
  // src/task-graph/Dataflow.ts
52
159
  var DATAFLOW_ALL_PORTS = "*";
53
160
  var DATAFLOW_ERROR_PORT = "[error]";
@@ -177,18 +284,62 @@ class Dataflow {
177
284
  return result;
178
285
  }
179
286
  toJSON() {
180
- return {
287
+ const base = {
181
288
  sourceTaskId: this.sourceTaskId,
182
289
  sourceTaskPortId: this.sourceTaskPortId,
183
290
  targetTaskId: this.targetTaskId,
184
291
  targetTaskPortId: this.targetTaskPortId
185
292
  };
293
+ if (this._transforms.length > 0) {
294
+ base.transforms = this._transforms.map((s) => ({ id: s.id, params: s.params }));
295
+ }
296
+ return base;
186
297
  }
187
298
  _compatibilityCache;
299
+ _transforms = [];
300
+ getTransforms() {
301
+ return this._transforms;
302
+ }
303
+ setTransforms(steps) {
304
+ this._transforms = steps.map((s) => ({ id: s.id, params: s.params }));
305
+ this.invalidateCompatibilityCache();
306
+ }
307
+ addTransform(step) {
308
+ this._transforms.push({ id: step.id, params: step.params });
309
+ this.invalidateCompatibilityCache();
310
+ }
311
+ removeTransform(index) {
312
+ this._transforms.splice(index, 1);
313
+ this.invalidateCompatibilityCache();
314
+ }
315
+ async applyTransforms(registry) {
316
+ if (this._transforms.length === 0)
317
+ return;
318
+ const defs = registry.get(TRANSFORM_DEFS);
319
+ let cur = this.value;
320
+ try {
321
+ for (const step of this._transforms) {
322
+ const def = defs.get(step.id);
323
+ if (!def) {
324
+ throw new Error(`Unknown transform: ${step.id}`);
325
+ }
326
+ cur = await def.apply(cur, step.params ?? {});
327
+ }
328
+ this.value = cur;
329
+ } catch (e) {
330
+ const error = e instanceof TaskError ? e : new TaskError(e instanceof Error ? e.message : String(e));
331
+ if (!(e instanceof TaskError) && e instanceof Error && e.stack) {
332
+ error.stack = e.stack;
333
+ }
334
+ this.error = error;
335
+ this.setStatus(TaskStatus.FAILED);
336
+ throw e;
337
+ }
338
+ }
188
339
  invalidateCompatibilityCache() {
189
340
  this._compatibilityCache = undefined;
190
341
  }
191
- semanticallyCompatible(graph, dataflow) {
342
+ semanticallyCompatible(graph, dataflow, registry) {
192
343
  const sourceTask = graph.getTask(dataflow.sourceTaskId);
193
344
  const targetTask = graph.getTask(dataflow.targetTaskId);
194
345
  const shouldCache = !(sourceTask.constructor.hasDynamicSchemas ?? true) && !(targetTask.constructor.hasDynamicSchemas ?? true);
@@ -217,7 +368,26 @@ class Dataflow {
217
368
  if (sourceSchemaProperty === undefined && sourceSchema.additionalProperties === true) {
218
369
  sourceSchemaProperty = true;
219
370
  }
220
- const result = areSemanticallyCompatible(sourceSchemaProperty, targetSchemaProperty);
371
+ let effectiveSourceSchema = sourceSchemaProperty;
372
+ if (this._transforms.length > 0) {
373
+ const defs = registry ? registry.get(TRANSFORM_DEFS) : TransformRegistry.all;
374
+ try {
375
+ let cur = effectiveSourceSchema === true ? {} : effectiveSourceSchema;
376
+ for (const step of this._transforms) {
377
+ const def = defs.get(step.id);
378
+ if (!def) {
379
+ return "incompatible";
380
+ }
381
+ cur = def.inferOutputSchema(cur, step.params ?? {});
382
+ }
383
+ effectiveSourceSchema = cur;
384
+ } catch {
385
+ if (shouldCache)
386
+ this._compatibilityCache = "incompatible";
387
+ return "incompatible";
388
+ }
389
+ }
390
+ const result = areSemanticallyCompatible(effectiveSourceSchema, targetSchemaProperty);
221
391
  if (shouldCache) {
222
392
  this._compatibilityCache = result;
223
393
  }
@@ -636,7 +806,8 @@ function computeGraphInputSchema(graph, options) {
636
806
  for (const [inputName, inputProp] of Object.entries(taskProperties)) {
637
807
  if (!properties[inputName]) {
638
808
  properties[inputName] = inputProp;
639
- if (taskInputSchema.required && taskInputSchema.required.includes(inputName)) {
809
+ const isRequired = taskInputSchema.required?.includes(inputName) === true && !(task.defaults && task.defaults[inputName] !== undefined);
810
+ if (isRequired) {
640
811
  required.push(inputName);
641
812
  }
642
813
  if (trackOrigins) {
@@ -949,6 +1120,7 @@ import { DirectedAcyclicGraph } from "@workglow/util/graph";
949
1120
 
950
1121
  // src/task/GraphAsTask.ts
951
1122
  import { getLogger as getLogger4 } from "@workglow/util";
1123
+ import { CycleError } from "@workglow/util/graph";
952
1124
  import { compileSchema as compileSchema2 } from "@workglow/util/schema";
953
1125
 
954
1126
  // src/task-graph/TaskGraphRunner.ts
@@ -956,15 +1128,15 @@ import {
956
1128
  collectPropertyValues,
957
1129
  getLogger as getLogger3,
958
1130
  getTelemetryProvider as getTelemetryProvider2,
959
- globalServiceRegistry as globalServiceRegistry2,
1131
+ globalServiceRegistry as globalServiceRegistry3,
960
1132
  ServiceRegistry as ServiceRegistry2,
961
1133
  SpanStatusCode as SpanStatusCode2,
962
1134
  uuid4 as uuid43
963
1135
  } from "@workglow/util";
964
1136
 
965
1137
  // src/storage/TaskOutputRepository.ts
966
- import { createServiceToken, EventEmitter as EventEmitter2 } from "@workglow/util";
967
- var TASK_OUTPUT_REPOSITORY = createServiceToken("taskgraph.taskOutputRepository");
1138
+ import { createServiceToken as createServiceToken2, EventEmitter as EventEmitter2 } from "@workglow/util";
1139
+ var TASK_OUTPUT_REPOSITORY = createServiceToken2("taskgraph.taskOutputRepository");
968
1140
 
969
1141
  class TaskOutputRepository {
970
1142
  outputCompression;
@@ -1066,103 +1238,11 @@ function getNestedValue(obj, path) {
1066
1238
  import { deepEqual, EventEmitter as EventEmitter3, uuid4 as uuid42 } from "@workglow/util";
1067
1239
  import { compileSchema } from "@workglow/util/schema";
1068
1240
 
1069
- // src/task/TaskError.ts
1070
- import { BaseError } from "@workglow/util";
1071
-
1072
- class TaskError extends BaseError {
1073
- static type = "TaskError";
1074
- taskType;
1075
- taskId;
1076
- constructor(message) {
1077
- super(message);
1078
- }
1079
- }
1080
-
1081
- class TaskConfigurationError extends TaskError {
1082
- static type = "TaskConfigurationError";
1083
- constructor(message) {
1084
- super(message);
1085
- }
1086
- }
1087
-
1088
- class WorkflowError extends TaskError {
1089
- static type = "WorkflowError";
1090
- constructor(message) {
1091
- super(message);
1092
- }
1093
- }
1094
-
1095
- class TaskAbortedError extends TaskError {
1096
- static type = "TaskAbortedError";
1097
- constructor(message = "Task aborted") {
1098
- super(message);
1099
- }
1100
- }
1101
-
1102
- class TaskTimeoutError extends TaskAbortedError {
1103
- static type = "TaskTimeoutError";
1104
- constructor(timeoutMs) {
1105
- super(timeoutMs ? `Task timed out after ${timeoutMs}ms` : "Task timed out");
1106
- }
1107
- }
1108
-
1109
- class TaskGraphTimeoutError extends TaskTimeoutError {
1110
- static type = "TaskGraphTimeoutError";
1111
- constructor(timeoutMs) {
1112
- super(timeoutMs);
1113
- this.message = timeoutMs ? `Graph execution timed out after ${timeoutMs}ms` : "Graph execution timed out";
1114
- }
1115
- }
1116
-
1117
- class TaskFailedError extends TaskError {
1118
- static type = "TaskFailedError";
1119
- constructor(message = "Task failed") {
1120
- super(message);
1121
- }
1122
- }
1123
-
1124
- class JobTaskFailedError extends TaskFailedError {
1125
- static type = "JobTaskFailedError";
1126
- jobError;
1127
- constructor(err) {
1128
- super(String(err));
1129
- this.jobError = err;
1130
- }
1131
- }
1132
-
1133
- class TaskJSONError extends TaskError {
1134
- static type = "TaskJSONError";
1135
- constructor(message = "Error converting JSON to a Task") {
1136
- super(message);
1137
- }
1138
- }
1139
-
1140
- class TaskInvalidInputError extends TaskError {
1141
- static type = "TaskInvalidInputError";
1142
- constructor(message = "Invalid input data") {
1143
- super(message);
1144
- }
1145
- }
1146
-
1147
- class TaskEntitlementError extends TaskError {
1148
- static type = "TaskEntitlementError";
1149
- constructor(message = "Required entitlements denied") {
1150
- super(message);
1151
- }
1152
- }
1153
-
1154
- class TaskSerializationError extends TaskError {
1155
- static type = "TaskSerializationError";
1156
- constructor(taskType) {
1157
- super(`Task "${taskType}" cannot be serialized: config contains non-serializable values. ` + `Use a declarative config alternative or remove function-valued config properties.`);
1158
- }
1159
- }
1160
-
1161
1241
  // src/task/TaskRunner.ts
1162
1242
  import {
1163
1243
  getLogger,
1164
1244
  getTelemetryProvider,
1165
- globalServiceRegistry,
1245
+ globalServiceRegistry as globalServiceRegistry2,
1166
1246
  SpanStatusCode
1167
1247
  } from "@workglow/util";
1168
1248
 
@@ -1278,7 +1358,7 @@ class TaskRunner {
1278
1358
  task;
1279
1359
  abortController;
1280
1360
  outputCache;
1281
- registry = globalServiceRegistry;
1361
+ registry = globalServiceRegistry2;
1282
1362
  resourceScope;
1283
1363
  inputStreams;
1284
1364
  timeoutTimer;
@@ -1549,7 +1629,7 @@ class TaskRunner {
1549
1629
  });
1550
1630
  const cache = config.outputCache ?? this.task.runConfig?.outputCache;
1551
1631
  if (cache === true) {
1552
- let instance = globalServiceRegistry.get(TASK_OUTPUT_REPOSITORY);
1632
+ let instance = globalServiceRegistry2.get(TASK_OUTPUT_REPOSITORY);
1553
1633
  this.outputCache = instance;
1554
1634
  } else if (cache === false) {
1555
1635
  this.outputCache = undefined;
@@ -2436,7 +2516,7 @@ class ConditionalTask extends Task {
2436
2516
  }
2437
2517
 
2438
2518
  // src/task/EntitlementEnforcer.ts
2439
- import { createServiceToken as createServiceToken3 } from "@workglow/util";
2519
+ import { createServiceToken as createServiceToken4 } from "@workglow/util";
2440
2520
 
2441
2521
  // src/task/EntitlementPolicy.ts
2442
2522
  var EMPTY_POLICY = Object.freeze({
@@ -2473,9 +2553,14 @@ function evaluatePolicy(policy, required) {
2473
2553
  }
2474
2554
  return results;
2475
2555
  }
2556
+ function can(policy, id, resources) {
2557
+ const required = resources !== undefined ? { id, resources } : { id };
2558
+ const [result] = evaluatePolicy(policy, { entitlements: [required] });
2559
+ return result;
2560
+ }
2476
2561
 
2477
2562
  // src/task/EntitlementResolver.ts
2478
- import { createServiceToken as createServiceToken2 } from "@workglow/util";
2563
+ import { createServiceToken as createServiceToken3 } from "@workglow/util";
2479
2564
  var PERMISSIVE_RESOLVER = {
2480
2565
  lookup: () => "grant",
2481
2566
  prompt: async () => "grant",
@@ -2486,9 +2571,19 @@ var DENY_ALL_RESOLVER = {
2486
2571
  prompt: async () => "deny",
2487
2572
  save: () => {}
2488
2573
  };
2489
- var ENTITLEMENT_RESOLVER = createServiceToken2("workglow.entitlementResolver");
2574
+ var ENTITLEMENT_RESOLVER = createServiceToken3("workglow.entitlementResolver");
2490
2575
 
2491
2576
  // src/task/EntitlementEnforcer.ts
2577
+ function formatEntitlementDenial(denial) {
2578
+ switch (denial.reason) {
2579
+ case "policy-deny":
2580
+ return `${denial.entitlement.id} (denied by rule ${denial.matchedRule.id})`;
2581
+ case "user-deny":
2582
+ return `${denial.entitlement.id} (denied by user)`;
2583
+ case "default-deny":
2584
+ return `${denial.entitlement.id} (no matching grant)`;
2585
+ }
2586
+ }
2492
2587
  var PERMISSIVE_ENFORCER = {
2493
2588
  checkAll: async () => [],
2494
2589
  checkTask: async () => []
@@ -2499,24 +2594,35 @@ function createPolicyEnforcer(policy, resolver = PERMISSIVE_RESOLVER) {
2499
2594
  const denied = [];
2500
2595
  for (const result of results) {
2501
2596
  if (result.verdict === "denied") {
2502
- denied.push(result.entitlement);
2597
+ if (result.matchedRule) {
2598
+ denied.push({
2599
+ entitlement: result.entitlement,
2600
+ reason: "policy-deny",
2601
+ matchedRule: result.matchedRule
2602
+ });
2603
+ } else {
2604
+ denied.push({ entitlement: result.entitlement, reason: "default-deny" });
2605
+ }
2503
2606
  } else if (result.verdict === "ask") {
2504
2607
  const request = {
2505
2608
  entitlement: result.entitlement,
2506
2609
  taskType: taskType ?? "unknown",
2507
2610
  taskId: taskId ?? "unknown"
2508
2611
  };
2509
- const saved = resolver.lookup(request);
2510
- if (saved !== undefined) {
2511
- if (saved === "deny") {
2512
- denied.push(result.entitlement);
2513
- }
2514
- continue;
2612
+ let answer = resolver.lookup(request);
2613
+ if (answer === undefined) {
2614
+ answer = await resolver.prompt(request);
2615
+ resolver.save(request, answer);
2515
2616
  }
2516
- const answer = await resolver.prompt(request);
2517
- resolver.save(request, answer);
2518
2617
  if (answer === "deny") {
2519
- denied.push(result.entitlement);
2618
+ if (!result.matchedRule) {
2619
+ throw new Error(`Invariant violation: ask verdict for "${result.entitlement.id}" is missing matchedRule`);
2620
+ }
2621
+ denied.push({
2622
+ entitlement: result.entitlement,
2623
+ reason: "user-deny",
2624
+ matchedRule: result.matchedRule
2625
+ });
2520
2626
  }
2521
2627
  }
2522
2628
  }
@@ -2538,7 +2644,7 @@ function createScopedEnforcer(grants) {
2538
2644
  function createGrantListEnforcer(grants) {
2539
2645
  return createScopedEnforcer(grants.map((id) => ({ id })));
2540
2646
  }
2541
- var ENTITLEMENT_ENFORCER = createServiceToken3("workglow.entitlementEnforcer");
2647
+ var ENTITLEMENT_ENFORCER = createServiceToken4("workglow.entitlementEnforcer");
2542
2648
 
2543
2649
  // src/task-graph/TaskGraphScheduler.ts
2544
2650
  class TopologicalScheduler {
@@ -2702,7 +2808,7 @@ class TaskGraphRunner {
2702
2808
  graph;
2703
2809
  outputCache;
2704
2810
  accumulateLeafOutputs = true;
2705
- registry = globalServiceRegistry2;
2811
+ registry = globalServiceRegistry3;
2706
2812
  resourceScope;
2707
2813
  abortController;
2708
2814
  inProgressTasks = new Map;
@@ -2784,6 +2890,21 @@ class TaskGraphRunner {
2784
2890
  }
2785
2891
  async runGraphReactive(input = {}, config) {
2786
2892
  await this.handleStartReactive(config);
2893
+ const telemetry = getTelemetryProvider2();
2894
+ const telemetryEnabled = telemetry.isEnabled;
2895
+ const reactiveRunId = telemetryEnabled ? uuid43() : "";
2896
+ let reactiveSpan;
2897
+ if (telemetryEnabled) {
2898
+ reactiveSpan = telemetry.startSpan("workglow.graph.runReactive", {
2899
+ attributes: {
2900
+ "workglow.graph.reactive.run_id": reactiveRunId,
2901
+ "workglow.graph.task_count": this.graph.getTasks().length,
2902
+ "workglow.graph.dataflow_count": this.graph.getDataflows().length
2903
+ }
2904
+ });
2905
+ }
2906
+ const t0 = telemetryEnabled ? performance.now() : 0;
2907
+ const taskTimings = [];
2787
2908
  const results = [];
2788
2909
  try {
2789
2910
  for await (const task of this.reactiveScheduler.tasks()) {
@@ -2793,20 +2914,68 @@ class TaskGraphRunner {
2793
2914
  this.copyInputFromEdgesToNode(task);
2794
2915
  }
2795
2916
  const taskInput = isRootTask ? input : {};
2796
- const taskResult = await task.runReactive(taskInput);
2797
- await this.pushOutputFromNodeToEdges(task, taskResult);
2798
- if (this.graph.getTargetDataflows(task.id).length === 0) {
2799
- results.push({
2800
- id: task.id,
2801
- type: task.constructor.runtype || task.constructor.type,
2802
- data: taskResult
2803
- });
2917
+ if (telemetryEnabled) {
2918
+ const taskType = String(task.constructor.runtype || task.constructor.type || "?");
2919
+ const tReactive = performance.now();
2920
+ const taskResult = await task.runReactive(taskInput);
2921
+ const runReactiveMs = performance.now() - tReactive;
2922
+ const tPush = performance.now();
2923
+ await this.pushOutputFromNodeToEdges(task, taskResult);
2924
+ const pushOutputMs = performance.now() - tPush;
2925
+ taskTimings.push({ id: task.id, type: taskType, runReactiveMs, pushOutputMs });
2926
+ if (this.graph.getTargetDataflows(task.id).length === 0) {
2927
+ results.push({
2928
+ id: task.id,
2929
+ type: task.constructor.runtype || task.constructor.type,
2930
+ data: taskResult
2931
+ });
2932
+ }
2933
+ } else {
2934
+ const taskResult = await task.runReactive(taskInput);
2935
+ await this.pushOutputFromNodeToEdges(task, taskResult);
2936
+ if (this.graph.getTargetDataflows(task.id).length === 0) {
2937
+ results.push({
2938
+ id: task.id,
2939
+ type: task.constructor.runtype || task.constructor.type,
2940
+ data: taskResult
2941
+ });
2942
+ }
2804
2943
  }
2805
2944
  }
2806
2945
  await this.handleCompleteReactive();
2946
+ if (reactiveSpan) {
2947
+ const totalMs = performance.now() - t0;
2948
+ reactiveSpan.setAttributes({
2949
+ "workglow.graph.reactive.duration_ms": Math.round(totalMs * 1000) / 1000,
2950
+ "workglow.graph.reactive.tasks_executed": taskTimings.length
2951
+ });
2952
+ reactiveSpan.setStatus(SpanStatusCode2.OK);
2953
+ reactiveSpan.end();
2954
+ getLogger3().debug("task graph runReactive timings", {
2955
+ reactiveRunId,
2956
+ totalMs: Math.round(totalMs * 1000) / 1000,
2957
+ taskTimings
2958
+ });
2959
+ }
2807
2960
  return this.filterLeafResults(results);
2808
2961
  } catch (error) {
2809
2962
  await this.handleErrorReactive();
2963
+ if (reactiveSpan) {
2964
+ const totalMs = performance.now() - t0;
2965
+ const message = error instanceof Error ? error.message : String(error);
2966
+ reactiveSpan.setAttributes({
2967
+ "workglow.graph.reactive.duration_ms": Math.round(totalMs * 1000) / 1000,
2968
+ "workglow.graph.reactive.tasks_executed": taskTimings.length
2969
+ });
2970
+ reactiveSpan.setStatus(SpanStatusCode2.ERROR, message);
2971
+ reactiveSpan.end();
2972
+ getLogger3().debug("task graph runReactive failed", {
2973
+ reactiveRunId,
2974
+ totalMs: Math.round(totalMs * 1000) / 1000,
2975
+ taskTimings,
2976
+ error
2977
+ });
2978
+ }
2810
2979
  throw error;
2811
2980
  }
2812
2981
  }
@@ -2874,13 +3043,17 @@ class TaskGraphRunner {
2874
3043
  async pushOutputFromNodeToEdges(node, results) {
2875
3044
  const dataflows = this.graph.getTargetDataflows(node.id);
2876
3045
  for (const dataflow of dataflows) {
2877
- const compatibility = dataflow.semanticallyCompatible(this.graph, dataflow);
3046
+ if (dataflow.stream !== undefined)
3047
+ continue;
3048
+ const compatibility = dataflow.semanticallyCompatible(this.graph, dataflow, this.registry);
2878
3049
  if (compatibility === "static") {
2879
3050
  dataflow.setPortData(results);
3051
+ await dataflow.applyTransforms(this.registry);
2880
3052
  } else if (compatibility === "runtime") {
2881
3053
  const task = this.graph.getTask(dataflow.targetTaskId);
2882
3054
  const narrowed = await task.narrowInput({ ...results }, this.registry);
2883
3055
  dataflow.setPortData(narrowed);
3056
+ await dataflow.applyTransforms(this.registry);
2884
3057
  } else {
2885
3058
  const resultsKeys = Object.keys(results);
2886
3059
  if (resultsKeys.length > 0) {
@@ -2906,6 +3079,8 @@ class TaskGraphRunner {
2906
3079
  }
2907
3080
  const activeBranches = node.getActiveBranches();
2908
3081
  for (const dataflow of dataflows) {
3082
+ if (dataflow.status === TaskStatus.FAILED)
3083
+ continue;
2909
3084
  const branchId = portToBranch.get(dataflow.sourceTaskPortId);
2910
3085
  if (branchId !== undefined) {
2911
3086
  if (activeBranches.has(branchId)) {
@@ -2921,6 +3096,8 @@ class TaskGraphRunner {
2921
3096
  return;
2922
3097
  }
2923
3098
  dataflows.forEach((dataflow) => {
3099
+ if (dataflow.status === TaskStatus.FAILED)
3100
+ return;
2924
3101
  dataflow.setStatus(effectiveStatus);
2925
3102
  });
2926
3103
  }
@@ -3024,7 +3201,7 @@ class TaskGraphRunner {
3024
3201
  if (this.activeEnforcer && task.constructor.hasDynamicEntitlements) {
3025
3202
  const denied = await this.activeEnforcer.checkTask(task);
3026
3203
  if (denied.length > 0) {
3027
- throw new TaskEntitlementError(`Task ${task.constructor.type} denied entitlements: ${denied.map((e) => e.id).join(", ")}`);
3204
+ throw new TaskEntitlementError(`Task ${task.constructor.type} denied entitlements: ${denied.map(formatEntitlementDenial).join(", ")}`);
3028
3205
  }
3029
3206
  }
3030
3207
  if (isStreamable) {
@@ -3045,10 +3222,13 @@ class TaskGraphRunner {
3045
3222
  }
3046
3223
  async awaitStreamInputs(task) {
3047
3224
  const dataflows = this.graph.getSourceDataflows(task.id);
3048
- const streamPromises = dataflows.filter((df) => df.stream !== undefined).map((df) => df.awaitStreamValue());
3049
- if (streamPromises.length > 0) {
3050
- await Promise.all(streamPromises);
3051
- }
3225
+ const streamingDataflows = dataflows.filter((df) => df.stream !== undefined);
3226
+ if (streamingDataflows.length === 0)
3227
+ return;
3228
+ await Promise.all(streamingDataflows.map(async (df) => {
3229
+ await df.awaitStreamValue();
3230
+ await df.applyTransforms(this.registry);
3231
+ }));
3052
3232
  }
3053
3233
  async runStreamingTask(task, input) {
3054
3234
  const streamMode = getOutputStreamMode(task.outputSchema());
@@ -3183,7 +3363,7 @@ class TaskGraphRunner {
3183
3363
  if (config?.registry !== undefined) {
3184
3364
  this.registry = config.registry;
3185
3365
  } else if (this.registry === undefined) {
3186
- this.registry = new ServiceRegistry2(globalServiceRegistry2.container.createChildContainer());
3366
+ this.registry = new ServiceRegistry2(globalServiceRegistry3.container.createChildContainer());
3187
3367
  }
3188
3368
  if (config?.resourceScope !== undefined) {
3189
3369
  this.resourceScope = config.resourceScope;
@@ -3247,7 +3427,7 @@ class TaskGraphRunner {
3247
3427
  const enforcer = this.registry.get(ENTITLEMENT_ENFORCER);
3248
3428
  const denied = await enforcer.checkAll(computeGraphEntitlements(this.graph));
3249
3429
  if (denied.length > 0) {
3250
- throw new TaskEntitlementError(`Denied entitlements: ${denied.map((e) => e.id).join(", ")}`);
3430
+ throw new TaskEntitlementError(`Denied entitlements: ${denied.map(formatEntitlementDenial).join(", ")}`);
3251
3431
  }
3252
3432
  this.activeEnforcer = enforcer;
3253
3433
  } else {
@@ -3591,11 +3771,23 @@ class GraphAsTask extends Task {
3591
3771
  yield { type: "finish", data: input };
3592
3772
  }
3593
3773
  }
3594
- regenerateGraph() {
3595
- this._inputSchemaNode = undefined;
3596
- this.events.emit("regenerate");
3597
- this.emitEntitlementChange();
3598
- }
3774
+ validateAcyclic() {
3775
+ if (!this.hasChildren())
3776
+ return;
3777
+ if (!this.subGraph.isAcyclic()) {
3778
+ throw new CycleError(`${this.type} (${this.id}): subgraph contains a cycle — loop tasks must wrap an acyclic subgraph.`);
3779
+ }
3780
+ for (const child of this.subGraph.getTasks()) {
3781
+ if (child instanceof GraphAsTask) {
3782
+ child.validateAcyclic();
3783
+ }
3784
+ }
3785
+ }
3786
+ regenerateGraph() {
3787
+ this._inputSchemaNode = undefined;
3788
+ this.events.emit("regenerate");
3789
+ this.emitEntitlementChange();
3790
+ }
3599
3791
  _entitlementUnsub;
3600
3792
  _subscribingEntitlements = false;
3601
3793
  _subscribeToSubGraphEntitlements(graph) {
@@ -3741,7 +3933,7 @@ function convertPipeFunctionToTask(fn, config) {
3741
3933
  properties: {
3742
3934
  [DATAFLOW_ALL_PORTS]: {}
3743
3935
  },
3744
- additionalProperties: false
3936
+ additionalProperties: true
3745
3937
  };
3746
3938
  };
3747
3939
  static outputSchema = () => {
@@ -3750,7 +3942,7 @@ function convertPipeFunctionToTask(fn, config) {
3750
3942
  properties: {
3751
3943
  [DATAFLOW_ALL_PORTS]: {}
3752
3944
  },
3753
- additionalProperties: false
3945
+ additionalProperties: true
3754
3946
  };
3755
3947
  };
3756
3948
  static cacheable = false;
@@ -3859,6 +4051,9 @@ class TaskGraph {
3859
4051
  topologicallySortedNodes() {
3860
4052
  return this._dag.topologicallySortedNodes();
3861
4053
  }
4054
+ isAcyclic() {
4055
+ return this._dag.isAcyclic();
4056
+ }
3862
4057
  addTask(task, config) {
3863
4058
  return this._dag.addNode(ensureTask(task, config));
3864
4059
  }
@@ -4132,7 +4327,315 @@ function serialGraph(tasks, inputHandle, outputHandle) {
4132
4327
  return graph;
4133
4328
  }
4134
4329
  // src/task-graph/Workflow.ts
4135
- import { EventEmitter as EventEmitter5, getLogger as getLogger5, uuid4 as uuid45 } from "@workglow/util";
4330
+ import { EventEmitter as EventEmitter5, getLogger as getLogger5, uuid4 as uuid46 } from "@workglow/util";
4331
+
4332
+ // src/task-graph/autoConnect.ts
4333
+ function autoConnect(graph, sourceTask, targetTask, options) {
4334
+ const matches = new Map;
4335
+ const sourceSchema = sourceTask.outputSchema();
4336
+ const targetSchema = targetTask.inputSchema();
4337
+ const providedInputKeys = options?.providedInputKeys ?? new Set;
4338
+ const connectedInputKeys = options?.connectedInputKeys ?? new Set;
4339
+ const earlierTasks = options?.earlierTasks ?? [];
4340
+ const dryRun = options?.dryRun ?? false;
4341
+ const addDataflow = (df) => {
4342
+ if (!dryRun)
4343
+ graph.addDataflow(df);
4344
+ };
4345
+ const getSpecificTypeIdentifiers = (schema) => {
4346
+ const formats = new Set;
4347
+ const ids = new Set;
4348
+ if (typeof schema === "boolean") {
4349
+ return { formats, ids };
4350
+ }
4351
+ const extractFromSchema = (s) => {
4352
+ if (!s || typeof s !== "object" || Array.isArray(s))
4353
+ return;
4354
+ if (s.format)
4355
+ formats.add(s.format);
4356
+ if (s.$id)
4357
+ ids.add(s.$id);
4358
+ };
4359
+ extractFromSchema(schema);
4360
+ const checkUnion = (schemas) => {
4361
+ if (!schemas)
4362
+ return;
4363
+ for (const s of schemas) {
4364
+ if (typeof s === "boolean")
4365
+ continue;
4366
+ extractFromSchema(s);
4367
+ if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
4368
+ extractFromSchema(s.items);
4369
+ }
4370
+ }
4371
+ };
4372
+ checkUnion(schema.oneOf);
4373
+ checkUnion(schema.anyOf);
4374
+ if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
4375
+ extractFromSchema(schema.items);
4376
+ }
4377
+ return { formats, ids };
4378
+ };
4379
+ const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
4380
+ if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
4381
+ return fromPortOutputSchema === true && toPortInputSchema === true;
4382
+ }
4383
+ const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
4384
+ const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
4385
+ for (const format of outputIds.formats) {
4386
+ if (inputIds.formats.has(format)) {
4387
+ return true;
4388
+ }
4389
+ }
4390
+ for (const id of outputIds.ids) {
4391
+ if (inputIds.ids.has(id)) {
4392
+ return true;
4393
+ }
4394
+ }
4395
+ if (requireSpecificType) {
4396
+ return false;
4397
+ }
4398
+ const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
4399
+ if (!idTypeBlank)
4400
+ return false;
4401
+ if (fromPortOutputSchema.type === toPortInputSchema.type)
4402
+ return true;
4403
+ const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
4404
+ if (typeof schema === "boolean")
4405
+ return schema;
4406
+ return schema.type === fromPortOutputSchema.type;
4407
+ }) ?? false;
4408
+ const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
4409
+ if (typeof schema === "boolean")
4410
+ return schema;
4411
+ return schema.type === fromPortOutputSchema.type;
4412
+ }) ?? false;
4413
+ return matchesOneOf || matchesAnyOf;
4414
+ };
4415
+ const makeMatch = (fromSchema, toSchema, fromTaskId, toTaskId, comparator) => {
4416
+ if (typeof fromSchema === "object") {
4417
+ if (toSchema === true || typeof toSchema === "object" && toSchema.additionalProperties === true) {
4418
+ const outputKeys = Object.keys(fromSchema.properties || {});
4419
+ if (outputKeys.length > 0) {
4420
+ for (const fromOutputPortId of outputKeys) {
4421
+ if (matches.has(fromOutputPortId))
4422
+ continue;
4423
+ matches.set(fromOutputPortId, fromOutputPortId);
4424
+ addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
4425
+ }
4426
+ } else if (fromSchema.additionalProperties === true) {
4427
+ const sourceGraphTask = graph.getTask(fromTaskId);
4428
+ if (sourceGraphTask && sourceGraphTask.constructor.passthroughInputsToOutputs === true) {
4429
+ const incomingDfs = graph.getSourceDataflows(fromTaskId);
4430
+ for (const df of incomingDfs) {
4431
+ const portId = df.targetTaskPortId;
4432
+ if (portId === DATAFLOW_ALL_PORTS)
4433
+ continue;
4434
+ if (matches.has(portId))
4435
+ continue;
4436
+ if (connectedInputKeys.has(portId))
4437
+ continue;
4438
+ matches.set(portId, portId);
4439
+ addDataflow(new Dataflow(fromTaskId, portId, toTaskId, portId));
4440
+ }
4441
+ }
4442
+ }
4443
+ return;
4444
+ }
4445
+ }
4446
+ if (typeof fromSchema === "object" && fromSchema.additionalProperties === true && typeof toSchema === "object" && (sourceTask.type === "InputTask" || sourceTask.type === "OutputTask")) {
4447
+ for (const toInputPortId of Object.keys(toSchema.properties || {})) {
4448
+ if (matches.has(toInputPortId))
4449
+ continue;
4450
+ if (connectedInputKeys.has(toInputPortId))
4451
+ continue;
4452
+ matches.set(toInputPortId, toInputPortId);
4453
+ addDataflow(new Dataflow(fromTaskId, toInputPortId, toTaskId, toInputPortId));
4454
+ }
4455
+ return;
4456
+ }
4457
+ if (typeof fromSchema === "boolean" || typeof toSchema === "boolean") {
4458
+ return;
4459
+ }
4460
+ for (const [toInputPortId, toPortInputSchema] of Object.entries(toSchema.properties || {})) {
4461
+ if (matches.has(toInputPortId))
4462
+ continue;
4463
+ if (connectedInputKeys.has(toInputPortId))
4464
+ continue;
4465
+ const candidates = [];
4466
+ for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(fromSchema.properties || {})) {
4467
+ if (comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
4468
+ candidates.push(fromOutputPortId);
4469
+ }
4470
+ }
4471
+ if (candidates.length === 0)
4472
+ continue;
4473
+ let winner = candidates[0];
4474
+ if (candidates.length > 1) {
4475
+ const targetStreamMode = getPortStreamMode(toSchema, toInputPortId);
4476
+ const streamMatch = candidates.find((portId) => getPortStreamMode(fromSchema, portId) === targetStreamMode);
4477
+ if (streamMatch)
4478
+ winner = streamMatch;
4479
+ }
4480
+ matches.set(toInputPortId, winner);
4481
+ addDataflow(new Dataflow(fromTaskId, winner, toTaskId, toInputPortId));
4482
+ }
4483
+ };
4484
+ makeMatch(sourceSchema, targetSchema, sourceTask.id, targetTask.id, ([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
4485
+ const outputPortIdMatch = fromOutputPortId === toInputPortId;
4486
+ const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
4487
+ const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
4488
+ return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
4489
+ });
4490
+ makeMatch(sourceSchema, targetSchema, sourceTask.id, targetTask.id, ([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
4491
+ return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
4492
+ });
4493
+ const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
4494
+ const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r) && !connectedInputKeys.has(r));
4495
+ let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
4496
+ if (unmatchedRequired.length > 0 && earlierTasks.length > 0) {
4497
+ for (let i = 0;i < earlierTasks.length && unmatchedRequired.length > 0; i++) {
4498
+ const earlierTask = earlierTasks[i];
4499
+ const earlierOutputSchema = earlierTask.outputSchema();
4500
+ if (earlierTask.type === "InputTask") {
4501
+ const inputConfig = earlierTask.config;
4502
+ const inputSchema = inputConfig?.inputSchema ?? inputConfig?.outputSchema;
4503
+ const isManualSchema = inputSchema && typeof inputSchema === "object" && inputSchema["x-ui-manual"] === true;
4504
+ const inputProperties = isManualSchema && inputSchema && typeof inputSchema === "object" && "properties" in inputSchema && inputSchema.properties && typeof inputSchema.properties === "object" ? new Set(Object.keys(inputSchema.properties)) : undefined;
4505
+ for (const requiredInputId of [...unmatchedRequired]) {
4506
+ if (matches.has(requiredInputId))
4507
+ continue;
4508
+ if (inputProperties && !inputProperties.has(requiredInputId))
4509
+ continue;
4510
+ matches.set(requiredInputId, requiredInputId);
4511
+ addDataflow(new Dataflow(earlierTask.id, requiredInputId, targetTask.id, requiredInputId));
4512
+ }
4513
+ unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
4514
+ continue;
4515
+ }
4516
+ const makeMatchFromEarlier = (comparator) => {
4517
+ if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
4518
+ return;
4519
+ }
4520
+ for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
4521
+ for (const requiredInputId of unmatchedRequired) {
4522
+ const toPortInputSchema = targetSchema.properties?.[requiredInputId];
4523
+ if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
4524
+ matches.set(requiredInputId, fromOutputPortId);
4525
+ addDataflow(new Dataflow(earlierTask.id, fromOutputPortId, targetTask.id, requiredInputId));
4526
+ }
4527
+ }
4528
+ }
4529
+ };
4530
+ makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
4531
+ const outputPortIdMatch = fromOutputPortId === toInputPortId;
4532
+ const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
4533
+ const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
4534
+ return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
4535
+ });
4536
+ makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
4537
+ return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
4538
+ });
4539
+ unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
4540
+ }
4541
+ }
4542
+ const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
4543
+ if (stillUnmatchedRequired.length > 0) {
4544
+ return {
4545
+ matches,
4546
+ error: `Could not find matches for required inputs [${stillUnmatchedRequired.join(", ")}] of ${targetTask.type}. ` + `Attempted to match from ${sourceTask.type} and earlier tasks.`,
4547
+ unmatchedRequired: stillUnmatchedRequired
4548
+ };
4549
+ }
4550
+ if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
4551
+ const existingTargetConnections = graph.getSourceDataflows(targetTask.id);
4552
+ if (existingTargetConnections.length > 0) {
4553
+ return { matches, unmatchedRequired: [] };
4554
+ }
4555
+ const hasRequiredInputs = requiredInputs.size > 0;
4556
+ const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
4557
+ const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
4558
+ if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
4559
+ return {
4560
+ matches,
4561
+ 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.`,
4562
+ unmatchedRequired: []
4563
+ };
4564
+ }
4565
+ }
4566
+ return {
4567
+ matches,
4568
+ unmatchedRequired: []
4569
+ };
4570
+ }
4571
+
4572
+ // src/task-graph/ConditionalBuilder.ts
4573
+ import { uuid4 as uuid45 } from "@workglow/util";
4574
+ class ConditionalBuilder {
4575
+ workflow;
4576
+ condition;
4577
+ thenSpec;
4578
+ elseSpec;
4579
+ constructor(workflow, condition) {
4580
+ this.workflow = workflow;
4581
+ this.condition = condition;
4582
+ }
4583
+ then(taskClass, input, config) {
4584
+ this.thenSpec = { taskClass, input, config };
4585
+ return this;
4586
+ }
4587
+ else(taskClass, input, config) {
4588
+ this.elseSpec = { taskClass, input, config };
4589
+ return this;
4590
+ }
4591
+ endIf() {
4592
+ if (!this.thenSpec) {
4593
+ throw new WorkflowError(".endIf() called without a prior .then(...) call");
4594
+ }
4595
+ const thenPort = "then";
4596
+ const elsePort = "else";
4597
+ const branches = [
4598
+ {
4599
+ id: thenPort,
4600
+ condition: this.condition,
4601
+ outputPort: thenPort
4602
+ }
4603
+ ];
4604
+ if (this.elseSpec) {
4605
+ branches.push({
4606
+ id: elsePort,
4607
+ condition: (input) => !this.condition(input),
4608
+ outputPort: elsePort
4609
+ });
4610
+ }
4611
+ const conditionalTask = new ConditionalTask({
4612
+ id: uuid45(),
4613
+ branches,
4614
+ exclusive: true,
4615
+ defaultBranch: this.elseSpec ? elsePort : undefined
4616
+ });
4617
+ this.workflow.graph.addTask(conditionalTask);
4618
+ const thenTask = instantiate(this.thenSpec);
4619
+ this.workflow.graph.addTask(thenTask);
4620
+ this.workflow.graph.addDataflow(new Dataflow(conditionalTask.id, thenPort, thenTask.id, "*"));
4621
+ if (this.elseSpec) {
4622
+ const elseTask = instantiate(this.elseSpec);
4623
+ this.workflow.graph.addTask(elseTask);
4624
+ this.workflow.graph.addDataflow(new Dataflow(conditionalTask.id, elsePort, elseTask.id, "*"));
4625
+ }
4626
+ return this.workflow;
4627
+ }
4628
+ }
4629
+ function instantiate(spec) {
4630
+ const config = {
4631
+ id: uuid45(),
4632
+ ...spec.config,
4633
+ defaults: spec.input
4634
+ };
4635
+ return new spec.taskClass(config);
4636
+ }
4637
+
4638
+ // src/task-graph/Workflow.ts
4136
4639
  function getLastTask(workflow) {
4137
4640
  const tasks = workflow.graph.getTasks();
4138
4641
  return tasks.length > 0 ? tasks[tasks.length - 1] : undefined;
@@ -4275,7 +4778,7 @@ class Workflow {
4275
4778
  this._error = "";
4276
4779
  const parent = getLastTask(this);
4277
4780
  const task = this.addTaskToGraph(taskClass, {
4278
- id: uuid45(),
4781
+ id: uuid46(),
4279
4782
  ...config,
4280
4783
  defaults: input
4281
4784
  });
@@ -4429,8 +4932,10 @@ class Workflow {
4429
4932
  static parallel(args, mergeFn) {
4430
4933
  return parallel(args, mergeFn ?? PROPERTY_ARRAY, new Workflow);
4431
4934
  }
4432
- rename(source, target, index = -1) {
4935
+ rename(source, target, indexOrOptions = -1) {
4433
4936
  this._error = "";
4937
+ const index = typeof indexOrOptions === "number" ? indexOrOptions : indexOrOptions.index ?? -1;
4938
+ const transforms = typeof indexOrOptions === "number" ? undefined : indexOrOptions.transforms;
4434
4939
  const nodes = this._graph.getTasks();
4435
4940
  if (-index > nodes.length) {
4436
4941
  const errorMsg = `Back index greater than number of tasks`;
@@ -4453,7 +4958,10 @@ class Workflow {
4453
4958
  getLogger5().error(this._error);
4454
4959
  throw new WorkflowError(errorMsg);
4455
4960
  }
4456
- this._dataFlows.push(new Dataflow(lastNode.id, source, undefined, target));
4961
+ const df = new Dataflow(lastNode.id, source, undefined, target);
4962
+ if (transforms && transforms.length > 0)
4963
+ df.setTransforms(transforms);
4964
+ this._dataFlows.push(df);
4457
4965
  return this;
4458
4966
  }
4459
4967
  onError(handler) {
@@ -4556,7 +5064,11 @@ class Workflow {
4556
5064
  addLoopTask(taskClass, config = {}) {
4557
5065
  this._error = "";
4558
5066
  const parent = getLastTask(this);
4559
- const task = this.addTaskToGraph(taskClass, { id: uuid45(), ...config });
5067
+ const schema = taskClass.configSchema?.();
5068
+ const required = typeof schema === "object" && schema !== null ? schema.required : undefined;
5069
+ const needsMaxIterations = Array.isArray(required) && required.includes("maxIterations");
5070
+ const resolvedConfig = needsMaxIterations && config.maxIterations === undefined ? { ...config, maxIterations: "unbounded" } : config;
5071
+ const task = this.addTaskToGraph(taskClass, { id: uuid46(), ...resolvedConfig });
4560
5072
  if (this._dataFlows.length > 0) {
4561
5073
  this._dataFlows.forEach((dataflow) => {
4562
5074
  const taskSchema = task.inputSchema();
@@ -4576,6 +5088,9 @@ class Workflow {
4576
5088
  }
4577
5089
  return loopBuilder;
4578
5090
  }
5091
+ if(condition) {
5092
+ return new ConditionalBuilder(this, condition);
5093
+ }
4579
5094
  autoConnectLoopTask(pending) {
4580
5095
  if (!pending)
4581
5096
  return;
@@ -4696,243 +5211,14 @@ class Workflow {
4696
5211
  }
4697
5212
  static AutoConnectOptions = Symbol("AutoConnectOptions");
4698
5213
  static autoConnect(graph, sourceTask, targetTask, options) {
4699
- const matches = new Map;
4700
- const sourceSchema = sourceTask.outputSchema();
4701
- const targetSchema = targetTask.inputSchema();
4702
- const providedInputKeys = options?.providedInputKeys ?? new Set;
4703
- const connectedInputKeys = options?.connectedInputKeys ?? new Set;
4704
- const earlierTasks = options?.earlierTasks ?? [];
4705
- const getSpecificTypeIdentifiers = (schema) => {
4706
- const formats = new Set;
4707
- const ids = new Set;
4708
- if (typeof schema === "boolean") {
4709
- return { formats, ids };
4710
- }
4711
- const extractFromSchema = (s) => {
4712
- if (!s || typeof s !== "object" || Array.isArray(s))
4713
- return;
4714
- if (s.format)
4715
- formats.add(s.format);
4716
- if (s.$id)
4717
- ids.add(s.$id);
4718
- };
4719
- extractFromSchema(schema);
4720
- const checkUnion = (schemas) => {
4721
- if (!schemas)
4722
- return;
4723
- for (const s of schemas) {
4724
- if (typeof s === "boolean")
4725
- continue;
4726
- extractFromSchema(s);
4727
- if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
4728
- extractFromSchema(s.items);
4729
- }
4730
- }
4731
- };
4732
- checkUnion(schema.oneOf);
4733
- checkUnion(schema.anyOf);
4734
- if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
4735
- extractFromSchema(schema.items);
4736
- }
4737
- return { formats, ids };
4738
- };
4739
- const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
4740
- if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
4741
- return fromPortOutputSchema === true && toPortInputSchema === true;
4742
- }
4743
- const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
4744
- const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
4745
- for (const format of outputIds.formats) {
4746
- if (inputIds.formats.has(format)) {
4747
- return true;
4748
- }
4749
- }
4750
- for (const id of outputIds.ids) {
4751
- if (inputIds.ids.has(id)) {
4752
- return true;
4753
- }
4754
- }
4755
- if (requireSpecificType) {
4756
- return false;
4757
- }
4758
- const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
4759
- if (!idTypeBlank)
4760
- return false;
4761
- if (fromPortOutputSchema.type === toPortInputSchema.type)
4762
- return true;
4763
- const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
4764
- if (typeof schema === "boolean")
4765
- return schema;
4766
- return schema.type === fromPortOutputSchema.type;
4767
- }) ?? false;
4768
- const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
4769
- if (typeof schema === "boolean")
4770
- return schema;
4771
- return schema.type === fromPortOutputSchema.type;
4772
- }) ?? false;
4773
- return matchesOneOf || matchesAnyOf;
4774
- };
4775
- const makeMatch = (fromSchema, toSchema, fromTaskId, toTaskId, comparator) => {
4776
- if (typeof fromSchema === "object") {
4777
- if (toSchema === true || typeof toSchema === "object" && toSchema.additionalProperties === true) {
4778
- const outputKeys = Object.keys(fromSchema.properties || {});
4779
- if (outputKeys.length > 0) {
4780
- for (const fromOutputPortId of outputKeys) {
4781
- if (matches.has(fromOutputPortId))
4782
- continue;
4783
- matches.set(fromOutputPortId, fromOutputPortId);
4784
- graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
4785
- }
4786
- } else if (fromSchema.additionalProperties === true) {
4787
- const sourceGraphTask = graph.getTask(fromTaskId);
4788
- if (sourceGraphTask && sourceGraphTask.constructor.passthroughInputsToOutputs === true) {
4789
- const incomingDfs = graph.getSourceDataflows(fromTaskId);
4790
- for (const df of incomingDfs) {
4791
- const portId = df.targetTaskPortId;
4792
- if (portId === DATAFLOW_ALL_PORTS)
4793
- continue;
4794
- if (matches.has(portId))
4795
- continue;
4796
- if (connectedInputKeys.has(portId))
4797
- continue;
4798
- matches.set(portId, portId);
4799
- graph.addDataflow(new Dataflow(fromTaskId, portId, toTaskId, portId));
4800
- }
4801
- }
4802
- }
4803
- return;
4804
- }
4805
- }
4806
- if (typeof fromSchema === "object" && fromSchema.additionalProperties === true && typeof toSchema === "object" && (sourceTask.type === "InputTask" || sourceTask.type === "OutputTask")) {
4807
- for (const toInputPortId of Object.keys(toSchema.properties || {})) {
4808
- if (matches.has(toInputPortId))
4809
- continue;
4810
- if (connectedInputKeys.has(toInputPortId))
4811
- continue;
4812
- matches.set(toInputPortId, toInputPortId);
4813
- graph.addDataflow(new Dataflow(fromTaskId, toInputPortId, toTaskId, toInputPortId));
4814
- }
4815
- return;
4816
- }
4817
- if (typeof fromSchema === "boolean" || typeof toSchema === "boolean") {
4818
- return;
4819
- }
4820
- for (const [toInputPortId, toPortInputSchema] of Object.entries(toSchema.properties || {})) {
4821
- if (matches.has(toInputPortId))
4822
- continue;
4823
- if (connectedInputKeys.has(toInputPortId))
4824
- continue;
4825
- const candidates = [];
4826
- for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(fromSchema.properties || {})) {
4827
- if (comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
4828
- candidates.push(fromOutputPortId);
4829
- }
4830
- }
4831
- if (candidates.length === 0)
4832
- continue;
4833
- let winner = candidates[0];
4834
- if (candidates.length > 1) {
4835
- const targetStreamMode = getPortStreamMode(toSchema, toInputPortId);
4836
- const streamMatch = candidates.find((portId) => getPortStreamMode(fromSchema, portId) === targetStreamMode);
4837
- if (streamMatch)
4838
- winner = streamMatch;
4839
- }
4840
- matches.set(toInputPortId, winner);
4841
- graph.addDataflow(new Dataflow(fromTaskId, winner, toTaskId, toInputPortId));
4842
- }
4843
- };
4844
- makeMatch(sourceSchema, targetSchema, sourceTask.id, targetTask.id, ([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
4845
- const outputPortIdMatch = fromOutputPortId === toInputPortId;
4846
- const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
4847
- const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
4848
- return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
4849
- });
4850
- makeMatch(sourceSchema, targetSchema, sourceTask.id, targetTask.id, ([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
4851
- return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
4852
- });
4853
- const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
4854
- const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r) && !connectedInputKeys.has(r));
4855
- let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
4856
- if (unmatchedRequired.length > 0 && earlierTasks.length > 0) {
4857
- for (let i = 0;i < earlierTasks.length && unmatchedRequired.length > 0; i++) {
4858
- const earlierTask = earlierTasks[i];
4859
- const earlierOutputSchema = earlierTask.outputSchema();
4860
- if (earlierTask.type === "InputTask") {
4861
- const inputConfig = earlierTask.config;
4862
- const inputSchema = inputConfig?.inputSchema ?? inputConfig?.outputSchema;
4863
- const isManualSchema = inputSchema && typeof inputSchema === "object" && inputSchema["x-ui-manual"] === true;
4864
- const inputProperties = isManualSchema && inputSchema && typeof inputSchema === "object" && "properties" in inputSchema && inputSchema.properties && typeof inputSchema.properties === "object" ? new Set(Object.keys(inputSchema.properties)) : undefined;
4865
- for (const requiredInputId of [...unmatchedRequired]) {
4866
- if (matches.has(requiredInputId))
4867
- continue;
4868
- if (inputProperties && !inputProperties.has(requiredInputId))
4869
- continue;
4870
- matches.set(requiredInputId, requiredInputId);
4871
- graph.addDataflow(new Dataflow(earlierTask.id, requiredInputId, targetTask.id, requiredInputId));
4872
- }
4873
- unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
4874
- continue;
4875
- }
4876
- const makeMatchFromEarlier = (comparator) => {
4877
- if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
4878
- return;
4879
- }
4880
- for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
4881
- for (const requiredInputId of unmatchedRequired) {
4882
- const toPortInputSchema = targetSchema.properties?.[requiredInputId];
4883
- if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
4884
- matches.set(requiredInputId, fromOutputPortId);
4885
- graph.addDataflow(new Dataflow(earlierTask.id, fromOutputPortId, targetTask.id, requiredInputId));
4886
- }
4887
- }
4888
- }
4889
- };
4890
- makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
4891
- const outputPortIdMatch = fromOutputPortId === toInputPortId;
4892
- const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
4893
- const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
4894
- return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
4895
- });
4896
- makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
4897
- return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
4898
- });
4899
- unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
4900
- }
4901
- }
4902
- const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
4903
- if (stillUnmatchedRequired.length > 0) {
4904
- return {
4905
- matches,
4906
- error: `Could not find matches for required inputs [${stillUnmatchedRequired.join(", ")}] of ${targetTask.type}. ` + `Attempted to match from ${sourceTask.type} and earlier tasks.`,
4907
- unmatchedRequired: stillUnmatchedRequired
4908
- };
4909
- }
4910
- if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
4911
- const existingTargetConnections = graph.getSourceDataflows(targetTask.id);
4912
- if (existingTargetConnections.length > 0) {
4913
- return { matches, unmatchedRequired: [] };
4914
- }
4915
- const hasRequiredInputs = requiredInputs.size > 0;
4916
- const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
4917
- const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
4918
- if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
4919
- return {
4920
- matches,
4921
- 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.`,
4922
- unmatchedRequired: []
4923
- };
4924
- }
4925
- }
4926
- return {
4927
- matches,
4928
- unmatchedRequired: []
4929
- };
5214
+ return autoConnect(graph, sourceTask, targetTask, options);
4930
5215
  }
4931
5216
  finalizeTemplate() {
4932
5217
  if (!this._iteratorTask || this.graph.getTasks().length === 0) {
4933
5218
  return;
4934
5219
  }
4935
5220
  this._iteratorTask.subGraph = this.graph;
5221
+ this._iteratorTask.validateAcyclic();
4936
5222
  }
4937
5223
  finalizeAndReturn() {
4938
5224
  if (!this._parentWorkflow) {
@@ -4967,6 +5253,7 @@ function getMethodNameMap() {
4967
5253
  }
4968
5254
  var LOOP_TASK_TYPES = {
4969
5255
  MapTask: { method: "map", endMethod: "endMap" },
5256
+ ForEachTask: { method: "forEach", endMethod: "endForEach" },
4970
5257
  ReduceTask: { method: "reduce", endMethod: "endReduce" },
4971
5258
  WhileTask: { method: "while", endMethod: "endWhile" },
4972
5259
  GraphAsTask: { method: "group", endMethod: "endGroup" }
@@ -5149,7 +5436,8 @@ function extractLoopConfig(task) {
5149
5436
  }
5150
5437
  break;
5151
5438
  }
5152
- case "MapTask": {
5439
+ case "MapTask":
5440
+ case "ForEachTask": {
5153
5441
  if (rawConfig.preserveOrder !== undefined && rawConfig.preserveOrder !== true) {
5154
5442
  config.preserveOrder = rawConfig.preserveOrder;
5155
5443
  }
@@ -5162,16 +5450,22 @@ function extractLoopConfig(task) {
5162
5450
  if (rawConfig.batchSize !== undefined) {
5163
5451
  config.batchSize = rawConfig.batchSize;
5164
5452
  }
5453
+ if (rawConfig.maxIterations !== undefined) {
5454
+ config.maxIterations = rawConfig.maxIterations;
5455
+ }
5165
5456
  break;
5166
5457
  }
5167
5458
  case "ReduceTask": {
5168
5459
  if (rawConfig.initialValue !== undefined) {
5169
5460
  config.initialValue = rawConfig.initialValue;
5170
5461
  }
5462
+ if (rawConfig.maxIterations !== undefined) {
5463
+ config.maxIterations = rawConfig.maxIterations;
5464
+ }
5171
5465
  break;
5172
5466
  }
5173
5467
  case "WhileTask": {
5174
- if (rawConfig.maxIterations !== undefined && rawConfig.maxIterations !== 100) {
5468
+ if (rawConfig.maxIterations !== undefined) {
5175
5469
  config.maxIterations = rawConfig.maxIterations;
5176
5470
  }
5177
5471
  if (rawConfig.chainIterations !== undefined && rawConfig.chainIterations !== true) {
@@ -5281,6 +5575,304 @@ ${baseIndent}}`;
5281
5575
  function resetMethodNameCache() {
5282
5576
  methodNameCache = undefined;
5283
5577
  }
5578
+ // src/task-graph/transforms/pick.ts
5579
+ import { areSemanticallyCompatible as areSemanticallyCompatible2 } from "@workglow/util/schema";
5580
+ function walk(value, path) {
5581
+ if (value == null)
5582
+ return;
5583
+ const parts = path.split(".");
5584
+ let cur = value;
5585
+ for (const p of parts) {
5586
+ if (cur == null)
5587
+ return;
5588
+ cur = cur[p];
5589
+ }
5590
+ return cur;
5591
+ }
5592
+ function walkSchema(schema, path) {
5593
+ const parts = path.split(".");
5594
+ let cur = schema;
5595
+ for (const p of parts) {
5596
+ if (!cur || typeof cur !== "object")
5597
+ return {};
5598
+ if (cur.type !== "object" || !cur.properties || !cur.properties[p]) {
5599
+ return {};
5600
+ }
5601
+ cur = cur.properties[p];
5602
+ }
5603
+ return cur;
5604
+ }
5605
+ var pickTransform = {
5606
+ id: "pick",
5607
+ title: "Pick field",
5608
+ category: "Structural",
5609
+ paramsSchema: {
5610
+ type: "object",
5611
+ properties: {
5612
+ path: { type: "string", description: "Dotted property path" }
5613
+ },
5614
+ required: ["path"]
5615
+ },
5616
+ inferOutputSchema(inputSchema, params) {
5617
+ return walkSchema(inputSchema, params.path);
5618
+ },
5619
+ apply(value, params) {
5620
+ return walk(value, params.path);
5621
+ },
5622
+ suggestFromSchemas(source, target) {
5623
+ if (source.type !== "object" || !source.properties) {
5624
+ return;
5625
+ }
5626
+ const props = source.properties;
5627
+ for (const [name, propSchema] of Object.entries(props)) {
5628
+ const compat = areSemanticallyCompatible2(propSchema, target);
5629
+ if (compat === "static")
5630
+ return { score: 1, params: { path: name } };
5631
+ if (compat === "runtime")
5632
+ return { score: 0.6, params: { path: name } };
5633
+ }
5634
+ return;
5635
+ }
5636
+ };
5637
+
5638
+ // src/task-graph/transforms/index-access.ts
5639
+ import { areSemanticallyCompatible as areSemanticallyCompatible3 } from "@workglow/util/schema";
5640
+ function doIndex(value, idx) {
5641
+ if (!Array.isArray(value))
5642
+ return;
5643
+ const i = idx < 0 ? value.length + idx : idx;
5644
+ return value[i];
5645
+ }
5646
+ var indexTransform = {
5647
+ id: "index",
5648
+ title: "Array index",
5649
+ category: "Structural",
5650
+ paramsSchema: {
5651
+ type: "object",
5652
+ properties: {
5653
+ index: { type: "integer", description: "Array index (negative counts from end)" }
5654
+ },
5655
+ required: ["index"]
5656
+ },
5657
+ inferOutputSchema(inputSchema) {
5658
+ const s = inputSchema;
5659
+ if (s?.type === "array" && s.items)
5660
+ return s.items;
5661
+ return {};
5662
+ },
5663
+ apply(value, params) {
5664
+ return doIndex(value, params.index);
5665
+ },
5666
+ suggestFromSchemas(source, target) {
5667
+ const s = source;
5668
+ if (s?.type !== "array" || !s.items)
5669
+ return;
5670
+ const compat = areSemanticallyCompatible3(s.items, target);
5671
+ if (compat === "static")
5672
+ return { score: 0.9, params: { index: 0 } };
5673
+ if (compat === "runtime")
5674
+ return { score: 0.5, params: { index: 0 } };
5675
+ return;
5676
+ }
5677
+ };
5678
+
5679
+ // src/task-graph/transforms/coalesce.ts
5680
+ function stripNullable(schema) {
5681
+ const s = schema;
5682
+ if (!s || typeof s !== "object")
5683
+ return schema;
5684
+ if (Array.isArray(s.type)) {
5685
+ const nonNull = s.type.filter((t) => t !== "null");
5686
+ if (nonNull.length === 1)
5687
+ return { ...s, type: nonNull[0] };
5688
+ return { ...s, type: nonNull };
5689
+ }
5690
+ return schema;
5691
+ }
5692
+ var coalesceTransform = {
5693
+ id: "coalesce",
5694
+ title: "Coalesce null",
5695
+ category: "Conversion",
5696
+ paramsSchema: {
5697
+ type: "object",
5698
+ properties: { defaultValue: {} },
5699
+ required: ["defaultValue"]
5700
+ },
5701
+ inferOutputSchema: (input) => stripNullable(input),
5702
+ apply: (v, { defaultValue }) => v == null ? defaultValue : v
5703
+ };
5704
+
5705
+ // src/task-graph/transforms/string-casts.ts
5706
+ var stringSchema = { type: "string" };
5707
+ var uppercaseTransform = {
5708
+ id: "uppercase",
5709
+ title: "Uppercase",
5710
+ category: "String",
5711
+ paramsSchema: undefined,
5712
+ inferOutputSchema: () => stringSchema,
5713
+ apply: (v) => String(v ?? "").toUpperCase()
5714
+ };
5715
+ var lowercaseTransform = {
5716
+ id: "lowercase",
5717
+ title: "Lowercase",
5718
+ category: "String",
5719
+ paramsSchema: undefined,
5720
+ inferOutputSchema: () => stringSchema,
5721
+ apply: (v) => String(v ?? "").toLowerCase()
5722
+ };
5723
+ var truncateTransform = {
5724
+ id: "truncate",
5725
+ title: "Truncate",
5726
+ category: "String",
5727
+ paramsSchema: {
5728
+ type: "object",
5729
+ properties: { max: { type: "integer", minimum: 0 } },
5730
+ required: ["max"]
5731
+ },
5732
+ inferOutputSchema: () => stringSchema,
5733
+ apply: (v, { max }) => String(v ?? "").slice(0, max)
5734
+ };
5735
+ var substringTransform = {
5736
+ id: "substring",
5737
+ title: "Substring",
5738
+ category: "String",
5739
+ paramsSchema: {
5740
+ type: "object",
5741
+ properties: {
5742
+ start: { type: "integer" },
5743
+ end: { type: "integer" }
5744
+ },
5745
+ required: ["start"]
5746
+ },
5747
+ inferOutputSchema: () => stringSchema,
5748
+ apply: (v, { start, end }) => String(v ?? "").slice(start, end)
5749
+ };
5750
+
5751
+ // src/task-graph/transforms/date-conversions.ts
5752
+ var isoSchema = { type: "string", format: "date-time" };
5753
+ var numberSchema = { type: "number" };
5754
+ function hasDateTimeFormat(schema) {
5755
+ const s = schema;
5756
+ return s?.type === "string" && s.format === "date-time";
5757
+ }
5758
+ var unixToIsoDateTransform = {
5759
+ id: "unixToIsoDate",
5760
+ title: "Unix timestamp → ISO date",
5761
+ category: "Date",
5762
+ paramsSchema: {
5763
+ type: "object",
5764
+ properties: {
5765
+ unit: { type: "string", enum: ["s", "ms"] }
5766
+ },
5767
+ required: ["unit"]
5768
+ },
5769
+ inferOutputSchema: () => isoSchema,
5770
+ apply: (v, { unit }) => {
5771
+ const n = Number(v);
5772
+ return new Date(unit === "s" ? n * 1000 : n).toISOString();
5773
+ },
5774
+ suggestFromSchemas(source, target) {
5775
+ const s = source;
5776
+ if (s?.type !== "number" && s?.type !== "integer")
5777
+ return;
5778
+ if (!hasDateTimeFormat(target))
5779
+ return;
5780
+ return { score: 0.85, params: { unit: "s" } };
5781
+ }
5782
+ };
5783
+ var isoDateToUnixTransform = {
5784
+ id: "isoDateToUnix",
5785
+ title: "ISO date → Unix ms",
5786
+ category: "Date",
5787
+ paramsSchema: undefined,
5788
+ inferOutputSchema: () => numberSchema,
5789
+ apply: (v) => new Date(String(v)).getTime(),
5790
+ suggestFromSchemas(source, target) {
5791
+ if (!hasDateTimeFormat(source))
5792
+ return;
5793
+ const t = target;
5794
+ if (t?.type !== "number" && t?.type !== "integer")
5795
+ return;
5796
+ return { score: 0.9, params: {} };
5797
+ }
5798
+ };
5799
+
5800
+ // src/task-graph/transforms/scalar-conversions.ts
5801
+ var stringSchema2 = { type: "string" };
5802
+ var booleanSchema = { type: "boolean" };
5803
+ var numberToStringTransform = {
5804
+ id: "numberToString",
5805
+ title: "Number → String",
5806
+ category: "Conversion",
5807
+ paramsSchema: undefined,
5808
+ inferOutputSchema: () => stringSchema2,
5809
+ apply: (v) => String(v),
5810
+ suggestFromSchemas(source, target) {
5811
+ const s = source;
5812
+ const t = target;
5813
+ if ((s?.type === "number" || s?.type === "integer") && t?.type === "string") {
5814
+ return { score: 0.8, params: {} };
5815
+ }
5816
+ return;
5817
+ }
5818
+ };
5819
+ var toBooleanTransform = {
5820
+ id: "toBoolean",
5821
+ title: "To Boolean",
5822
+ category: "Conversion",
5823
+ paramsSchema: undefined,
5824
+ inferOutputSchema: () => booleanSchema,
5825
+ apply: (v) => {
5826
+ if (typeof v === "boolean")
5827
+ return v;
5828
+ if (typeof v === "number")
5829
+ return v !== 0;
5830
+ if (typeof v === "string")
5831
+ return v.toLowerCase() === "true" || v === "1";
5832
+ return Boolean(v);
5833
+ }
5834
+ };
5835
+ var stringifyTransform = {
5836
+ id: "stringify",
5837
+ title: "JSON.stringify",
5838
+ category: "Conversion",
5839
+ paramsSchema: undefined,
5840
+ inferOutputSchema: () => stringSchema2,
5841
+ apply: (v) => JSON.stringify(v),
5842
+ suggestFromSchemas(source, target) {
5843
+ const t = target;
5844
+ return t?.type === "string" ? { score: 0.4, params: {} } : undefined;
5845
+ }
5846
+ };
5847
+ var parseJsonTransform = {
5848
+ id: "parseJson",
5849
+ title: "Parse JSON",
5850
+ category: "Conversion",
5851
+ paramsSchema: undefined,
5852
+ inferOutputSchema: () => ({}),
5853
+ apply: (v) => JSON.parse(String(v))
5854
+ };
5855
+
5856
+ // src/task-graph/transforms/index.ts
5857
+ function registerBuiltInTransforms() {
5858
+ const all = [
5859
+ pickTransform,
5860
+ indexTransform,
5861
+ coalesceTransform,
5862
+ uppercaseTransform,
5863
+ lowercaseTransform,
5864
+ truncateTransform,
5865
+ substringTransform,
5866
+ unixToIsoDateTransform,
5867
+ isoDateToUnixTransform,
5868
+ numberToStringTransform,
5869
+ toBooleanTransform,
5870
+ stringifyTransform,
5871
+ parseJsonTransform
5872
+ ];
5873
+ for (const t of all)
5874
+ TransformRegistry.registerTransform(t);
5875
+ }
5284
5876
  // src/task/EntitlementProfiles.ts
5285
5877
  var BROWSER_GRANTS = [
5286
5878
  { id: Entitlements.NETWORK_HTTP },
@@ -5609,15 +6201,15 @@ async function compactSchemaInputs(input, schema, config, visited = new Set) {
5609
6201
  return compacted;
5610
6202
  }
5611
6203
  // src/task/IteratorTaskRunner.ts
5612
- import { uuid4 as uuid46 } from "@workglow/util";
6204
+ import { uuid4 as uuid47 } from "@workglow/util";
5613
6205
  class IteratorTaskRunner extends GraphAsTaskRunner {
5614
6206
  aggregatingParentMapProgress = false;
5615
6207
  mapPartialProgress = [];
5616
6208
  mapPartialIterationCount = 0;
5617
6209
  async executeTask(input) {
5618
6210
  let analysis = this.task.analyzeIterationInput(input);
5619
- const maxIterations = this.task.config.maxIterations;
5620
- if (maxIterations !== undefined && maxIterations > 0 && analysis.iterationCount > maxIterations) {
6211
+ const maxIterations = resolveIterationBound(this.task.config.maxIterations);
6212
+ if (analysis.iterationCount > maxIterations) {
5621
6213
  analysis = { ...analysis, iterationCount: maxIterations };
5622
6214
  }
5623
6215
  if (analysis.iterationCount === 0) {
@@ -5723,7 +6315,7 @@ class IteratorTaskRunner extends GraphAsTaskRunner {
5723
6315
  const idMap = new Map;
5724
6316
  for (const task of graph.getTasks()) {
5725
6317
  const ctor = task.constructor;
5726
- const newId = uuid46();
6318
+ const newId = uuid47();
5727
6319
  idMap.set(task.config.id, newId);
5728
6320
  const clonedConfig = { ...task.config, id: newId };
5729
6321
  const newTask = new ctor({ ...clonedConfig, defaults: task.defaults }, task.runConfig);
@@ -5793,15 +6385,24 @@ var ITERATOR_CONTEXT_SCHEMA = {
5793
6385
  }
5794
6386
  }
5795
6387
  };
6388
+ function resolveIterationBound(bound) {
6389
+ return bound === "unbounded" ? Number.POSITIVE_INFINITY : bound;
6390
+ }
5796
6391
  var iteratorTaskConfigSchema = {
5797
6392
  type: "object",
5798
6393
  properties: {
5799
6394
  ...graphAsTaskConfigSchema["properties"],
5800
6395
  concurrencyLimit: { type: "integer", minimum: 1 },
5801
6396
  batchSize: { type: "integer", minimum: 1 },
5802
- maxIterations: { type: "integer", minimum: 1 },
6397
+ maxIterations: {
6398
+ oneOf: [
6399
+ { type: "integer", minimum: 1 },
6400
+ { type: "string", const: "unbounded" }
6401
+ ]
6402
+ },
5803
6403
  iterationInputConfig: { type: "object", additionalProperties: true }
5804
6404
  },
6405
+ required: ["maxIterations"],
5805
6406
  additionalProperties: false
5806
6407
  };
5807
6408
  function isArrayVariant(schema) {
@@ -5916,6 +6517,12 @@ class IteratorTask extends GraphAsTask {
5916
6517
  static configSchema() {
5917
6518
  return iteratorTaskConfigSchema;
5918
6519
  }
6520
+ constructor(config = {}, runConfig = {}) {
6521
+ if (config.maxIterations === undefined) {
6522
+ throw new TaskConfigurationError(`${new.target.type ?? "IteratorTask"}: maxIterations is required. ` + `Pass a positive integer to cap iteration, or "unbounded" to opt out explicitly.`);
6523
+ }
6524
+ super(config, runConfig);
6525
+ }
5919
6526
  static getIterationContextSchema() {
5920
6527
  return ITERATOR_CONTEXT_SCHEMA;
5921
6528
  }
@@ -6305,13 +6912,19 @@ var whileTaskConfigSchema = {
6305
6912
  properties: {
6306
6913
  ...graphAsTaskConfigSchema["properties"],
6307
6914
  condition: {},
6308
- maxIterations: { type: "integer", minimum: 1 },
6915
+ maxIterations: {
6916
+ oneOf: [
6917
+ { type: "integer", minimum: 1 },
6918
+ { type: "string", const: "unbounded" }
6919
+ ]
6920
+ },
6309
6921
  chainIterations: { type: "boolean" },
6310
6922
  conditionField: { type: "string" },
6311
6923
  conditionOperator: { type: "string" },
6312
6924
  conditionValue: { type: "string" },
6313
6925
  iterationInputConfig: { type: "object", additionalProperties: true }
6314
6926
  },
6927
+ required: ["maxIterations"],
6315
6928
  additionalProperties: false
6316
6929
  };
6317
6930
 
@@ -6324,6 +6937,12 @@ class WhileTask extends GraphAsTask {
6324
6937
  static configSchema() {
6325
6938
  return whileTaskConfigSchema;
6326
6939
  }
6940
+ constructor(config = {}, runConfig = {}) {
6941
+ if (config.maxIterations === undefined) {
6942
+ throw new TaskConfigurationError(`${new.target.type ?? "WhileTask"}: maxIterations is required. ` + `Pass a positive integer to cap iteration, or "unbounded" to opt out explicitly.`);
6943
+ }
6944
+ super(config, runConfig);
6945
+ }
6327
6946
  static getIterationContextSchema() {
6328
6947
  return WHILE_CONTEXT_SCHEMA;
6329
6948
  }
@@ -6341,7 +6960,7 @@ class WhileTask extends GraphAsTask {
6341
6960
  return this.config.condition;
6342
6961
  }
6343
6962
  get maxIterations() {
6344
- return this.config.maxIterations ?? 100;
6963
+ return resolveIterationBound(this.config.maxIterations);
6345
6964
  }
6346
6965
  get chainIterations() {
6347
6966
  return this.config.chainIterations ?? true;
@@ -6872,8 +7491,8 @@ import {
6872
7491
  JobQueueServer
6873
7492
  } from "@workglow/job-queue";
6874
7493
  import { InMemoryQueueStorage } from "@workglow/storage";
6875
- import { createServiceToken as createServiceToken4, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
6876
- var JOB_QUEUE_FACTORY = createServiceToken4("taskgraph.jobQueueFactory");
7494
+ import { createServiceToken as createServiceToken5, globalServiceRegistry as globalServiceRegistry4 } from "@workglow/util";
7495
+ var JOB_QUEUE_FACTORY = createServiceToken5("taskgraph.jobQueueFactory");
6877
7496
  var defaultJobQueueFactory = async ({
6878
7497
  queueName,
6879
7498
  jobClass,
@@ -6900,7 +7519,7 @@ var defaultJobQueueFactory = async ({
6900
7519
  return { server, client, storage };
6901
7520
  };
6902
7521
  function registerJobQueueFactory(factory) {
6903
- globalServiceRegistry3.registerInstance(JOB_QUEUE_FACTORY, factory);
7522
+ globalServiceRegistry4.registerInstance(JOB_QUEUE_FACTORY, factory);
6904
7523
  }
6905
7524
  function createJobQueueFactoryWithOptions(defaultOptions = {}) {
6906
7525
  return async ({
@@ -6934,12 +7553,12 @@ function createJobQueueFactoryWithOptions(defaultOptions = {}) {
6934
7553
  };
6935
7554
  }
6936
7555
  function getJobQueueFactory() {
6937
- if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
7556
+ if (!globalServiceRegistry4.has(JOB_QUEUE_FACTORY)) {
6938
7557
  registerJobQueueFactory(defaultJobQueueFactory);
6939
7558
  }
6940
- return globalServiceRegistry3.get(JOB_QUEUE_FACTORY);
7559
+ return globalServiceRegistry4.get(JOB_QUEUE_FACTORY);
6941
7560
  }
6942
- if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
7561
+ if (!globalServiceRegistry4.has(JOB_QUEUE_FACTORY)) {
6943
7562
  registerJobQueueFactory(defaultJobQueueFactory);
6944
7563
  }
6945
7564
  // src/task/MapTask.ts
@@ -6948,8 +7567,10 @@ var mapTaskConfigSchema = {
6948
7567
  properties: {
6949
7568
  ...iteratorTaskConfigSchema["properties"],
6950
7569
  preserveOrder: { type: "boolean" },
6951
- flatten: { type: "boolean" }
7570
+ flatten: { type: "boolean" },
7571
+ discardResults: { type: "boolean" }
6952
7572
  },
7573
+ required: iteratorTaskConfigSchema.required,
6953
7574
  additionalProperties: false
6954
7575
  };
6955
7576
 
@@ -6982,6 +7603,9 @@ class MapTask extends IteratorTask {
6982
7603
  get flatten() {
6983
7604
  return this.config.flatten ?? false;
6984
7605
  }
7606
+ get discardResults() {
7607
+ return this.config.discardResults ?? false;
7608
+ }
6985
7609
  preserveIterationOrder() {
6986
7610
  return this.preserveOrder;
6987
7611
  }
@@ -7003,6 +7627,9 @@ class MapTask extends IteratorTask {
7003
7627
  return this.getWrappedOutputSchema();
7004
7628
  }
7005
7629
  collectResults(results) {
7630
+ if (this.discardResults) {
7631
+ return this.getEmptyResult();
7632
+ }
7006
7633
  const collected = super.collectResults(results);
7007
7634
  if (!this.flatten || typeof collected !== "object" || collected === null) {
7008
7635
  return collected;
@@ -7018,8 +7645,19 @@ class MapTask extends IteratorTask {
7018
7645
  return flattened;
7019
7646
  }
7020
7647
  }
7648
+
7649
+ class ForEachTask extends MapTask {
7650
+ static type = "ForEachTask";
7651
+ static title = "For Each";
7652
+ static description = "Runs a workflow per array item for side effects; discards collected results";
7653
+ constructor(config = {}, runConfig = {}) {
7654
+ super({ discardResults: true, ...config }, runConfig);
7655
+ }
7656
+ }
7021
7657
  Workflow.prototype.map = CreateLoopWorkflow(MapTask);
7022
7658
  Workflow.prototype.endMap = CreateEndLoopWorkflow("endMap");
7659
+ Workflow.prototype.forEach = CreateLoopWorkflow(ForEachTask);
7660
+ Workflow.prototype.endForEach = CreateEndLoopWorkflow("endForEach");
7023
7661
  // src/task/ReduceTask.ts
7024
7662
  var reduceTaskConfigSchema = {
7025
7663
  type: "object",
@@ -7027,6 +7665,7 @@ var reduceTaskConfigSchema = {
7027
7665
  ...iteratorTaskConfigSchema["properties"],
7028
7666
  initialValue: {}
7029
7667
  },
7668
+ required: iteratorTaskConfigSchema.required,
7030
7669
  additionalProperties: false
7031
7670
  };
7032
7671
 
@@ -7038,13 +7677,13 @@ class ReduceTask extends IteratorTask {
7038
7677
  static configSchema() {
7039
7678
  return reduceTaskConfigSchema;
7040
7679
  }
7041
- constructor(config = {}) {
7680
+ constructor(config = {}, runConfig = {}) {
7042
7681
  const reduceConfig = {
7043
7682
  ...config,
7044
7683
  concurrencyLimit: 1,
7045
7684
  batchSize: 1
7046
7685
  };
7047
- super(reduceConfig);
7686
+ super(reduceConfig, runConfig);
7048
7687
  }
7049
7688
  get initialValue() {
7050
7689
  return this.config.initialValue ?? {};
@@ -7116,9 +7755,9 @@ queueMicrotask(() => {
7116
7755
  });
7117
7756
  // src/task/TaskRegistry.ts
7118
7757
  import {
7119
- createServiceToken as createServiceToken5,
7758
+ createServiceToken as createServiceToken6,
7120
7759
  getLogger as getLogger6,
7121
- globalServiceRegistry as globalServiceRegistry4,
7760
+ globalServiceRegistry as globalServiceRegistry5,
7122
7761
  registerInputCompactor,
7123
7762
  registerInputResolver
7124
7763
  } from "@workglow/util";
@@ -7156,13 +7795,13 @@ var TaskRegistry = {
7156
7795
  registerTask,
7157
7796
  unregisterTask
7158
7797
  };
7159
- var TASK_CONSTRUCTORS = createServiceToken5("task.constructors");
7160
- globalServiceRegistry4.registerIfAbsent(TASK_CONSTRUCTORS, () => TaskRegistry.all, true);
7798
+ var TASK_CONSTRUCTORS = createServiceToken6("task.constructors");
7799
+ globalServiceRegistry5.registerIfAbsent(TASK_CONSTRUCTORS, () => TaskRegistry.all, true);
7161
7800
  function getGlobalTaskConstructors() {
7162
- return globalServiceRegistry4.get(TASK_CONSTRUCTORS);
7801
+ return globalServiceRegistry5.get(TASK_CONSTRUCTORS);
7163
7802
  }
7164
7803
  function setGlobalTaskConstructors(map) {
7165
- globalServiceRegistry4.registerInstance(TASK_CONSTRUCTORS, map);
7804
+ globalServiceRegistry5.registerInstance(TASK_CONSTRUCTORS, map);
7166
7805
  }
7167
7806
  function getTaskConstructors(registry) {
7168
7807
  if (!registry)
@@ -7259,7 +7898,11 @@ var createGraphFromGraphJSON = (graphJsonObj, registry, options) => {
7259
7898
  subGraph.addTask(createTaskFromGraphJSON(subitem, registry, options));
7260
7899
  }
7261
7900
  for (const subitem of graphJsonObj.dataflows) {
7262
- subGraph.addDataflow(new Dataflow(subitem.sourceTaskId, subitem.sourceTaskPortId, subitem.targetTaskId, subitem.targetTaskPortId));
7901
+ const dataflow = new Dataflow(subitem.sourceTaskId, subitem.sourceTaskPortId, subitem.targetTaskId, subitem.targetTaskPortId);
7902
+ if (subitem.transforms && subitem.transforms.length > 0) {
7903
+ dataflow.setTransforms(subitem.transforms);
7904
+ }
7905
+ subGraph.addDataflow(dataflow);
7263
7906
  }
7264
7907
  return subGraph;
7265
7908
  };
@@ -7317,8 +7960,8 @@ var registerBaseTasks = () => {
7317
7960
  return tasks;
7318
7961
  };
7319
7962
  // src/storage/TaskGraphRepository.ts
7320
- import { createServiceToken as createServiceToken6, EventEmitter as EventEmitter7 } from "@workglow/util";
7321
- var TASK_GRAPH_REPOSITORY = createServiceToken6("taskgraph.taskGraphRepository");
7963
+ import { createServiceToken as createServiceToken7, EventEmitter as EventEmitter7 } from "@workglow/util";
7964
+ var TASK_GRAPH_REPOSITORY = createServiceToken7("taskgraph.taskGraphRepository");
7322
7965
 
7323
7966
  class TaskGraphRepository {
7324
7967
  type = "TaskGraphRepository";
@@ -8113,7 +8756,13 @@ function installDevToolsFormatters() {
8113
8756
  export {
8114
8757
  wrapSchemaInArray,
8115
8758
  whileTaskConfigSchema,
8759
+ uppercaseTransform,
8760
+ unixToIsoDateTransform,
8761
+ truncateTransform,
8762
+ toBooleanTransform,
8116
8763
  taskPrototypeHasOwnExecute,
8764
+ substringTransform,
8765
+ stringifyTransform,
8117
8766
  setTaskQueueRegistry,
8118
8767
  setGlobalTaskConstructors,
8119
8768
  serialGraph,
@@ -8123,25 +8772,33 @@ export {
8123
8772
  scanGraphForCredentials,
8124
8773
  resourcePatternMatches,
8125
8774
  resolveSchemaInputs,
8775
+ resolveIterationBound,
8126
8776
  resetMethodNameCache,
8127
8777
  removeIterationProperties,
8128
8778
  registerJobQueueFactory,
8779
+ registerBuiltInTransforms,
8129
8780
  registerBaseTasks,
8130
8781
  reduceTaskConfigSchema,
8131
8782
  pipe,
8783
+ pickTransform,
8784
+ parseJsonTransform,
8132
8785
  parallel,
8786
+ numberToStringTransform,
8133
8787
  mergeResources,
8134
8788
  mergeEntitlements,
8135
8789
  mergeEntitlementPair,
8136
8790
  mergeChainedOutputToInput,
8137
8791
  mapTaskConfigSchema,
8792
+ lowercaseTransform,
8138
8793
  iteratorTaskConfigSchema,
8794
+ isoDateToUnixTransform,
8139
8795
  isTaskStreamable,
8140
8796
  isStrictArraySchema,
8141
8797
  isIterationProperty,
8142
8798
  isFlexibleSchema,
8143
8799
  isDarkMode,
8144
8800
  installDevToolsFormatters,
8801
+ indexTransform,
8145
8802
  hasVectorOutput,
8146
8803
  hasVectorLikeInput,
8147
8804
  hasStructuredOutput,
@@ -8167,6 +8824,7 @@ export {
8167
8824
  getFormatPrefix,
8168
8825
  getAppendPortId,
8169
8826
  formatValue,
8827
+ formatEntitlementDenial,
8170
8828
  findArrayPorts,
8171
8829
  filterIterationProperties,
8172
8830
  fallbackTaskConfigSchema,
@@ -8195,8 +8853,11 @@ export {
8195
8853
  computeGraphInputSchema,
8196
8854
  computeGraphEntitlements,
8197
8855
  compactSchemaInputs,
8856
+ coalesceTransform,
8857
+ can,
8198
8858
  calculateNodeDepths,
8199
8859
  buildIterationInputSchema,
8860
+ autoConnect,
8200
8861
  addIterationContextToSchema,
8201
8862
  addBoundaryNodesToGraphJson,
8202
8863
  addBoundaryNodesToDependencyJson,
@@ -8205,6 +8866,7 @@ export {
8205
8866
  WhileTaskRunner,
8206
8867
  WhileTask,
8207
8868
  WHILE_CONTEXT_SCHEMA,
8869
+ TransformRegistry,
8208
8870
  TaskTimeoutError,
8209
8871
  TaskStatus,
8210
8872
  TaskSerializationError,
@@ -8230,6 +8892,7 @@ export {
8230
8892
  TaskConfigSchema,
8231
8893
  TaskAbortedError,
8232
8894
  Task,
8895
+ TRANSFORM_DEFS,
8233
8896
  TASK_OUTPUT_REPOSITORY,
8234
8897
  TASK_GRAPH_REPOSITORY,
8235
8898
  TASK_CONSTRUCTORS,
@@ -8247,6 +8910,7 @@ export {
8247
8910
  GraphAsTaskRunner,
8248
8911
  GraphAsTask,
8249
8912
  GRAPH_RESULT_ARRAY,
8913
+ ForEachTask,
8250
8914
  FallbackTaskRunner,
8251
8915
  FallbackTask,
8252
8916
  EventTaskGraphToDagMapping,
@@ -8270,4 +8934,4 @@ export {
8270
8934
  BROWSER_GRANTS
8271
8935
  };
8272
8936
 
8273
- //# debugId=A2F9041FD815B90264756E2164756E21
8937
+ //# debugId=B2CBA2397CBCF51264756E2164756E21