ai-workflows 2.1.3 → 2.4.0

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 (188) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +14 -1
  3. package/README.md +2 -0
  4. package/dist/barrier.d.ts +6 -0
  5. package/dist/barrier.d.ts.map +1 -1
  6. package/dist/barrier.js +45 -7
  7. package/dist/barrier.js.map +1 -1
  8. package/dist/cascade-context.d.ts.map +1 -1
  9. package/dist/cascade-context.js +25 -25
  10. package/dist/cascade-context.js.map +1 -1
  11. package/dist/cascade-executor.d.ts.map +1 -1
  12. package/dist/cascade-executor.js +1 -1
  13. package/dist/cascade-executor.js.map +1 -1
  14. package/dist/context.d.ts.map +1 -1
  15. package/dist/context.js +23 -7
  16. package/dist/context.js.map +1 -1
  17. package/dist/cron-parser.d.ts +65 -0
  18. package/dist/cron-parser.d.ts.map +1 -0
  19. package/dist/cron-parser.js +294 -0
  20. package/dist/cron-parser.js.map +1 -0
  21. package/dist/cron-scheduler.d.ts +117 -0
  22. package/dist/cron-scheduler.d.ts.map +1 -0
  23. package/dist/cron-scheduler.js +176 -0
  24. package/dist/cron-scheduler.js.map +1 -0
  25. package/dist/database-context.d.ts +184 -0
  26. package/dist/database-context.d.ts.map +1 -0
  27. package/dist/database-context.js +428 -0
  28. package/dist/database-context.js.map +1 -0
  29. package/dist/digital-objects-adapter.d.ts +159 -0
  30. package/dist/digital-objects-adapter.d.ts.map +1 -0
  31. package/dist/digital-objects-adapter.js +229 -0
  32. package/dist/digital-objects-adapter.js.map +1 -0
  33. package/dist/durable-execution-cloudflare.d.ts +427 -0
  34. package/dist/durable-execution-cloudflare.d.ts.map +1 -0
  35. package/dist/durable-execution-cloudflare.js +510 -0
  36. package/dist/durable-execution-cloudflare.js.map +1 -0
  37. package/dist/durable-execution.d.ts +482 -0
  38. package/dist/durable-execution.d.ts.map +1 -0
  39. package/dist/durable-execution.js +594 -0
  40. package/dist/durable-execution.js.map +1 -0
  41. package/dist/durable-workflow.d.ts +176 -0
  42. package/dist/durable-workflow.d.ts.map +1 -0
  43. package/dist/durable-workflow.js +552 -0
  44. package/dist/durable-workflow.js.map +1 -0
  45. package/dist/graph/topological-sort.d.ts.map +1 -1
  46. package/dist/graph/topological-sort.js +5 -5
  47. package/dist/graph/topological-sort.js.map +1 -1
  48. package/dist/index.d.ts +4 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +15 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/logger.d.ts +101 -0
  53. package/dist/logger.d.ts.map +1 -0
  54. package/dist/logger.js +115 -0
  55. package/dist/logger.js.map +1 -0
  56. package/dist/on.d.ts.map +1 -1
  57. package/dist/on.js +3 -3
  58. package/dist/on.js.map +1 -1
  59. package/dist/runtime.d.ts +169 -0
  60. package/dist/runtime.d.ts.map +1 -0
  61. package/dist/runtime.js +275 -0
  62. package/dist/runtime.js.map +1 -0
  63. package/dist/send.d.ts.map +1 -1
  64. package/dist/send.js +4 -3
  65. package/dist/send.js.map +1 -1
  66. package/dist/telemetry.d.ts +150 -0
  67. package/dist/telemetry.d.ts.map +1 -0
  68. package/dist/telemetry.js +388 -0
  69. package/dist/telemetry.js.map +1 -0
  70. package/dist/timer-registry.d.ts +25 -0
  71. package/dist/timer-registry.d.ts.map +1 -1
  72. package/dist/timer-registry.js +42 -8
  73. package/dist/timer-registry.js.map +1 -1
  74. package/dist/types.d.ts +17 -6
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/types.js +1 -1
  77. package/dist/types.js.map +1 -1
  78. package/dist/worker/durable-step.d.ts +481 -0
  79. package/dist/worker/durable-step.d.ts.map +1 -0
  80. package/dist/worker/durable-step.js +606 -0
  81. package/dist/worker/durable-step.js.map +1 -0
  82. package/dist/worker/index.d.ts +106 -0
  83. package/dist/worker/index.d.ts.map +1 -0
  84. package/dist/worker/index.js +124 -0
  85. package/dist/worker/index.js.map +1 -0
  86. package/dist/worker/state-adapter.d.ts +230 -0
  87. package/dist/worker/state-adapter.d.ts.map +1 -0
  88. package/dist/worker/state-adapter.js +409 -0
  89. package/dist/worker/state-adapter.js.map +1 -0
  90. package/dist/worker/topological-executor.d.ts +282 -0
  91. package/dist/worker/topological-executor.d.ts.map +1 -0
  92. package/dist/worker/topological-executor.js +396 -0
  93. package/dist/worker/topological-executor.js.map +1 -0
  94. package/dist/worker/workflow-builder.d.ts +286 -0
  95. package/dist/worker/workflow-builder.d.ts.map +1 -0
  96. package/dist/worker/workflow-builder.js +565 -0
  97. package/dist/worker/workflow-builder.js.map +1 -0
  98. package/dist/worker.d.ts +800 -0
  99. package/dist/worker.d.ts.map +1 -0
  100. package/dist/worker.js +2428 -0
  101. package/dist/worker.js.map +1 -0
  102. package/dist/workflow-builder.d.ts +287 -0
  103. package/dist/workflow-builder.d.ts.map +1 -0
  104. package/dist/workflow-builder.js +762 -0
  105. package/dist/workflow-builder.js.map +1 -0
  106. package/dist/workflow.d.ts +14 -30
  107. package/dist/workflow.d.ts.map +1 -1
  108. package/dist/workflow.js +132 -292
  109. package/dist/workflow.js.map +1 -1
  110. package/examples/01-ecommerce-order-pipeline.ts +358 -0
  111. package/examples/02-content-moderation-cascade.ts +454 -0
  112. package/examples/03-scheduled-reporting-dependencies.ts +479 -0
  113. package/examples/04-database-persistence.ts +518 -0
  114. package/examples/README.md +173 -0
  115. package/package.json +30 -13
  116. package/src/__tests__/digital-objects-adapter.test.ts +274 -0
  117. package/src/__tests__/durable-workflow.test.ts +297 -0
  118. package/src/barrier.ts +48 -7
  119. package/src/cascade-context.ts +36 -29
  120. package/src/cascade-executor.ts +3 -2
  121. package/src/context.ts +41 -12
  122. package/src/cron-parser.ts +347 -0
  123. package/src/cron-scheduler.ts +239 -0
  124. package/src/database-context.ts +658 -0
  125. package/src/digital-objects-adapter.ts +351 -0
  126. package/src/durable-execution-cloudflare.ts +855 -0
  127. package/src/durable-execution.ts +1042 -0
  128. package/src/durable-workflow.ts +717 -0
  129. package/src/graph/topological-sort.ts +6 -8
  130. package/src/index.ts +69 -0
  131. package/src/logger.ts +148 -0
  132. package/src/on.ts +8 -9
  133. package/src/runtime.ts +436 -0
  134. package/src/send.ts +4 -5
  135. package/src/telemetry.ts +577 -0
  136. package/src/timer-registry.ts +44 -10
  137. package/src/types.ts +32 -17
  138. package/src/worker/durable-step.ts +976 -0
  139. package/src/worker/index.ts +216 -0
  140. package/src/worker/state-adapter.ts +589 -0
  141. package/src/worker/topological-executor.ts +625 -0
  142. package/src/worker/workflow-builder.ts +871 -0
  143. package/src/worker.ts +2906 -0
  144. package/src/workflow-builder.ts +1068 -0
  145. package/src/workflow.ts +188 -351
  146. package/test/barrier-join.test.ts +32 -24
  147. package/test/cascade-executor.test.ts +9 -16
  148. package/test/cron-parser.test.ts +314 -0
  149. package/test/cron-scheduler.test.ts +291 -0
  150. package/test/database-context.test.ts +770 -0
  151. package/test/db-provider-adapter.test.ts +862 -0
  152. package/test/durable-execution-cloudflare.test.ts +606 -0
  153. package/test/durable-execution-in-process.test.ts +286 -0
  154. package/test/durable-execution.test.ts +247 -0
  155. package/test/e2e/workflow-scenarios.e2e.test.ts +1039 -0
  156. package/test/integration.test.ts +442 -0
  157. package/test/rpc-surface.test.ts +946 -0
  158. package/test/runtime.test.ts +262 -0
  159. package/test/schedule-timer-cleanup.test.ts +30 -21
  160. package/test/send-race-conditions.test.ts +30 -40
  161. package/test/worker/durable-cascade.test.ts +1117 -0
  162. package/test/worker/durable-step.test.ts +723 -0
  163. package/test/worker/topological-executor.test.ts +1240 -0
  164. package/test/worker/workflow-builder.test.ts +1067 -0
  165. package/test/worker.test.ts +608 -0
  166. package/test/workflow-builder.test.ts +1670 -0
  167. package/test/workflow-cron.test.ts +256 -0
  168. package/test/workflow-state-adapter.test.ts +923 -0
  169. package/test/workflow.test.ts +25 -22
  170. package/tsconfig.json +3 -1
  171. package/vitest.config.ts +38 -1
  172. package/vitest.workers.config.ts +44 -0
  173. package/wrangler.jsonc +22 -0
  174. package/.turbo/turbo-test.log +0 -169
  175. package/LICENSE +0 -21
  176. package/src/context.js +0 -83
  177. package/src/every.js +0 -267
  178. package/src/index.js +0 -71
  179. package/src/on.js +0 -79
  180. package/src/send.js +0 -111
  181. package/src/types.js +0 -4
  182. package/src/workflow.js +0 -455
  183. package/test/context.test.js +0 -116
  184. package/test/every.test.js +0 -282
  185. package/test/on.test.js +0 -80
  186. package/test/send.test.js +0 -89
  187. package/test/workflow.test.js +0 -224
  188. package/vitest.config.js +0 -7
@@ -0,0 +1,282 @@
1
+ /**
2
+ * TopologicalExecutor - Parallel step execution based on dependency graph
3
+ *
4
+ * Executes workflow steps in parallel while respecting dependency ordering.
5
+ * Uses topological sort to determine execution levels and runs steps
6
+ * within each level concurrently.
7
+ *
8
+ * Features:
9
+ * - Parallel execution of independent steps
10
+ * - Durable execution via Cloudflare Workflows step.do()
11
+ * - Progress callbacks for monitoring
12
+ * - Error handling with rollback support
13
+ * - Visualization helpers for debugging
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { TopologicalExecutor, DurableGraph } from 'ai-workflows/worker'
18
+ *
19
+ * // Create executor with steps
20
+ * const executor = new TopologicalExecutor()
21
+ * .addStep('fetch-user', [], async (input) => {
22
+ * return { userId: input.id }
23
+ * })
24
+ * .addStep('fetch-orders', ['fetch-user'], async (input, results) => {
25
+ * return { orders: [] }
26
+ * })
27
+ * .addStep('fetch-prefs', ['fetch-user'], async (input, results) => {
28
+ * return { prefs: {} }
29
+ * })
30
+ * .addStep('aggregate', ['fetch-orders', 'fetch-prefs'], async (input, results) => {
31
+ * return { ...results['fetch-orders'], ...results['fetch-prefs'] }
32
+ * })
33
+ *
34
+ * // Execute with durable workflow
35
+ * const result = await executor.run(step, { id: 'user-123' })
36
+ * ```
37
+ *
38
+ * @packageDocumentation
39
+ */
40
+ import { DependencyGraph, type DependencyType } from '../dependency-graph.js';
41
+ import { type ExecutionLevel } from '../graph/topological-sort.js';
42
+ import type { WorkflowStep, StepConfig } from './durable-step.js';
43
+ /**
44
+ * Function signature for step execution
45
+ */
46
+ export type StepExecutor<TInput = unknown, TOutput = unknown> = (input: TInput, previousResults: Record<string, unknown>, context: ExecutionContext) => Promise<TOutput>;
47
+ /**
48
+ * Definition of a step in the execution graph
49
+ */
50
+ export interface StepDefinition<TInput = unknown, TOutput = unknown> {
51
+ /** Unique step identifier */
52
+ id: string;
53
+ /** IDs of steps this step depends on */
54
+ dependencies: string[];
55
+ /** The step function to execute */
56
+ executor: StepExecutor<TInput, TOutput>;
57
+ /** Optional step configuration (retries, timeout) */
58
+ config: StepConfig | undefined;
59
+ /** Dependency types (hard/soft) */
60
+ dependencyTypes: Record<string, DependencyType> | undefined;
61
+ }
62
+ /**
63
+ * Context provided during step execution
64
+ */
65
+ export interface ExecutionContext {
66
+ /** Current step ID */
67
+ stepId: string;
68
+ /** Current execution level */
69
+ level: number;
70
+ /** Total number of levels */
71
+ totalLevels: number;
72
+ /** Steps completed so far */
73
+ completedSteps: string[];
74
+ /** Attempt number for this step */
75
+ attempt: number;
76
+ }
77
+ /**
78
+ * Progress callback for monitoring execution
79
+ */
80
+ export interface ProgressCallback {
81
+ /** Called when a level starts */
82
+ onLevelStart?: (level: number, steps: string[]) => void;
83
+ /** Called when a step starts */
84
+ onStepStart?: (stepId: string, level: number) => void;
85
+ /** Called when a step completes */
86
+ onStepComplete?: (stepId: string, level: number, result: unknown, duration: number) => void;
87
+ /** Called when a step fails */
88
+ onStepError?: (stepId: string, level: number, error: Error) => void;
89
+ /** Called when a level completes */
90
+ onLevelComplete?: (level: number, results: Record<string, unknown>) => void;
91
+ }
92
+ /**
93
+ * Result of executing the graph
94
+ */
95
+ export interface ExecutionResult<T = Record<string, unknown>> {
96
+ /** All step results keyed by step ID */
97
+ results: T;
98
+ /** Execution metrics */
99
+ metrics: {
100
+ totalDuration: number;
101
+ levelDurations: number[];
102
+ stepDurations: Record<string, number>;
103
+ parallelEfficiency: number;
104
+ };
105
+ /** Steps that were skipped due to soft dependency failures */
106
+ skippedSteps: string[];
107
+ /** Execution order (actual order steps ran in) */
108
+ executionOrder: string[];
109
+ }
110
+ /**
111
+ * Configuration for TopologicalExecutor
112
+ */
113
+ export interface ExecutorConfig {
114
+ /** Continue execution on step failure (for soft dependencies) */
115
+ continueOnError?: boolean;
116
+ /** Maximum concurrency per level (default: unlimited) */
117
+ maxConcurrency?: number;
118
+ /** Progress callbacks */
119
+ progress?: ProgressCallback;
120
+ /** Default step config */
121
+ defaultStepConfig?: StepConfig;
122
+ }
123
+ /**
124
+ * TopologicalExecutor - Execute steps in parallel based on dependency graph
125
+ *
126
+ * Provides parallel execution of workflow steps while respecting dependencies.
127
+ * Steps at the same execution level run concurrently, while levels execute
128
+ * sequentially.
129
+ */
130
+ export declare class TopologicalExecutor<TInput = unknown> {
131
+ private steps;
132
+ private config;
133
+ constructor(config?: ExecutorConfig);
134
+ /**
135
+ * Add a step to the executor
136
+ *
137
+ * @param id - Unique step identifier
138
+ * @param dependencies - IDs of steps this step depends on
139
+ * @param executor - Function to execute
140
+ * @param config - Optional step configuration
141
+ * @returns this (for chaining)
142
+ */
143
+ addStep<TOutput>(id: string, dependencies: string[], executor: StepExecutor<TInput, TOutput>, config?: StepConfig): this;
144
+ /**
145
+ * Add a step with dependency type specification
146
+ */
147
+ addStepWithTypes<TOutput>(id: string, dependencies: Array<{
148
+ id: string;
149
+ type?: DependencyType;
150
+ }>, executor: StepExecutor<TInput, TOutput>, config?: StepConfig): this;
151
+ /**
152
+ * Get the dependency graph
153
+ */
154
+ getGraph(): DependencyGraph;
155
+ /**
156
+ * Get execution levels for parallel execution
157
+ */
158
+ getExecutionLevels(): ExecutionLevel[];
159
+ /**
160
+ * Validate the graph (check for cycles, missing deps)
161
+ */
162
+ validate(): {
163
+ valid: boolean;
164
+ errors: string[];
165
+ };
166
+ /**
167
+ * Execute the graph with durable semantics
168
+ *
169
+ * @param workflowStep - Cloudflare Workflows step object
170
+ * @param input - Input to pass to all steps
171
+ * @returns Execution result with all step outputs
172
+ */
173
+ run(workflowStep: WorkflowStep, input: TInput): Promise<ExecutionResult<Record<string, unknown>>>;
174
+ /**
175
+ * Execute a single step with durability
176
+ */
177
+ private executeStep;
178
+ /**
179
+ * Visualize the execution graph as DOT format
180
+ */
181
+ toDot(): string;
182
+ /**
183
+ * Get graph as JSON for debugging
184
+ */
185
+ toJSON(): {
186
+ steps: StepDefinition[];
187
+ levels: ExecutionLevel[];
188
+ };
189
+ }
190
+ /**
191
+ * DurableGraph - Dependency graph with durable execution support
192
+ *
193
+ * Extends DependencyGraph with execution capabilities using Cloudflare
194
+ * Workflows durability. Provides a higher-level API for building and
195
+ * executing workflow step graphs.
196
+ *
197
+ * @example
198
+ * ```typescript
199
+ * const graph = new DurableGraph<{ userId: string }>()
200
+ * .node('fetch-user', async (input) => {
201
+ * return await fetchUser(input.userId)
202
+ * })
203
+ * .node('fetch-orders', ['fetch-user'], async (input, results) => {
204
+ * return await fetchOrders(results['fetch-user'].id)
205
+ * })
206
+ *
207
+ * const result = await graph.execute(step, { userId: '123' })
208
+ * ```
209
+ */
210
+ export declare class DurableGraph<TInput = unknown> {
211
+ private executor;
212
+ private nodeMetadata;
213
+ constructor(config?: ExecutorConfig);
214
+ /**
215
+ * Add a node (step) to the graph
216
+ *
217
+ * @param id - Unique node identifier
218
+ * @param executor - Function to execute (if no dependencies)
219
+ */
220
+ node<TOutput>(id: string, executor: StepExecutor<TInput, TOutput>): this;
221
+ /**
222
+ * Add a node with dependencies
223
+ *
224
+ * @param id - Unique node identifier
225
+ * @param dependencies - IDs of nodes this node depends on
226
+ * @param executor - Function to execute
227
+ */
228
+ node<TOutput>(id: string, dependencies: string[], executor: StepExecutor<TInput, TOutput>): this;
229
+ /**
230
+ * Add a node with dependencies and config
231
+ *
232
+ * @param id - Unique node identifier
233
+ * @param dependencies - IDs of nodes this node depends on
234
+ * @param executor - Function to execute
235
+ * @param config - Step configuration
236
+ */
237
+ node<TOutput>(id: string, dependencies: string[], executor: StepExecutor<TInput, TOutput>, config: StepConfig): this;
238
+ /**
239
+ * Add metadata to a node
240
+ */
241
+ describe(id: string, metadata: {
242
+ description?: string;
243
+ tags?: string[];
244
+ }): this;
245
+ /**
246
+ * Get execution levels
247
+ */
248
+ levels(): ExecutionLevel[];
249
+ /**
250
+ * Validate the graph
251
+ */
252
+ validate(): {
253
+ valid: boolean;
254
+ errors: string[];
255
+ };
256
+ /**
257
+ * Execute the graph with durable semantics
258
+ */
259
+ execute(workflowStep: WorkflowStep, input: TInput): Promise<ExecutionResult<Record<string, unknown>>>;
260
+ /**
261
+ * Get DOT visualization
262
+ */
263
+ toDot(): string;
264
+ /**
265
+ * Get as JSON
266
+ */
267
+ toJSON(): ReturnType<TopologicalExecutor<TInput>['toJSON']> & {
268
+ metadata: Record<string, {
269
+ description?: string;
270
+ tags?: string[];
271
+ }>;
272
+ };
273
+ }
274
+ /**
275
+ * Create a new TopologicalExecutor
276
+ */
277
+ export declare function createExecutor<TInput = unknown>(config?: ExecutorConfig): TopologicalExecutor<TInput>;
278
+ /**
279
+ * Create a new DurableGraph
280
+ */
281
+ export declare function createGraph<TInput = unknown>(config?: ExecutorConfig): DurableGraph<TInput>;
282
+ //# sourceMappingURL=topological-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topological-executor.d.ts","sourceRoot":"","sources":["../../src/worker/topological-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,396 @@
1
+ /**
2
+ * TopologicalExecutor - Parallel step execution based on dependency graph
3
+ *
4
+ * Executes workflow steps in parallel while respecting dependency ordering.
5
+ * Uses topological sort to determine execution levels and runs steps
6
+ * within each level concurrently.
7
+ *
8
+ * Features:
9
+ * - Parallel execution of independent steps
10
+ * - Durable execution via Cloudflare Workflows step.do()
11
+ * - Progress callbacks for monitoring
12
+ * - Error handling with rollback support
13
+ * - Visualization helpers for debugging
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { TopologicalExecutor, DurableGraph } from 'ai-workflows/worker'
18
+ *
19
+ * // Create executor with steps
20
+ * const executor = new TopologicalExecutor()
21
+ * .addStep('fetch-user', [], async (input) => {
22
+ * return { userId: input.id }
23
+ * })
24
+ * .addStep('fetch-orders', ['fetch-user'], async (input, results) => {
25
+ * return { orders: [] }
26
+ * })
27
+ * .addStep('fetch-prefs', ['fetch-user'], async (input, results) => {
28
+ * return { prefs: {} }
29
+ * })
30
+ * .addStep('aggregate', ['fetch-orders', 'fetch-prefs'], async (input, results) => {
31
+ * return { ...results['fetch-orders'], ...results['fetch-prefs'] }
32
+ * })
33
+ *
34
+ * // Execute with durable workflow
35
+ * const result = await executor.run(step, { id: 'user-123' })
36
+ * ```
37
+ *
38
+ * @packageDocumentation
39
+ */
40
+ import { DependencyGraph, CircularDependencyError, MissingDependencyError, } from '../dependency-graph.js';
41
+ import { topologicalSort, getExecutionLevels, CycleDetectedError, } from '../graph/topological-sort.js';
42
+ // ============================================================================
43
+ // TopologicalExecutor
44
+ // ============================================================================
45
+ /**
46
+ * TopologicalExecutor - Execute steps in parallel based on dependency graph
47
+ *
48
+ * Provides parallel execution of workflow steps while respecting dependencies.
49
+ * Steps at the same execution level run concurrently, while levels execute
50
+ * sequentially.
51
+ */
52
+ export class TopologicalExecutor {
53
+ steps = new Map();
54
+ config;
55
+ constructor(config = {}) {
56
+ this.config = config;
57
+ }
58
+ /**
59
+ * Add a step to the executor
60
+ *
61
+ * @param id - Unique step identifier
62
+ * @param dependencies - IDs of steps this step depends on
63
+ * @param executor - Function to execute
64
+ * @param config - Optional step configuration
65
+ * @returns this (for chaining)
66
+ */
67
+ addStep(id, dependencies, executor, config) {
68
+ this.steps.set(id, {
69
+ id,
70
+ dependencies,
71
+ executor: executor,
72
+ config: config ?? this.config.defaultStepConfig,
73
+ dependencyTypes: undefined,
74
+ });
75
+ return this;
76
+ }
77
+ /**
78
+ * Add a step with dependency type specification
79
+ */
80
+ addStepWithTypes(id, dependencies, executor, config) {
81
+ const depIds = dependencies.map((d) => d.id);
82
+ const depTypes = {};
83
+ for (const dep of dependencies) {
84
+ depTypes[dep.id] = dep.type ?? 'hard';
85
+ }
86
+ this.steps.set(id, {
87
+ id,
88
+ dependencies: depIds,
89
+ executor: executor,
90
+ config: config ?? this.config.defaultStepConfig,
91
+ dependencyTypes: depTypes,
92
+ });
93
+ return this;
94
+ }
95
+ /**
96
+ * Get the dependency graph
97
+ */
98
+ getGraph() {
99
+ const graph = new DependencyGraph();
100
+ // Add nodes in topological order to ensure dependencies exist
101
+ const nodes = Array.from(this.steps.values()).map((s) => ({
102
+ id: s.id,
103
+ dependencies: s.dependencies,
104
+ }));
105
+ const sorted = topologicalSort(nodes);
106
+ if (sorted.hasCycle) {
107
+ throw new CycleDetectedError(sorted.cyclePath ?? ['unknown']);
108
+ }
109
+ // Add nodes without dependencies first
110
+ for (const id of sorted.order) {
111
+ const step = this.steps.get(id);
112
+ if (step.dependencies.length === 0) {
113
+ graph.addNode(id);
114
+ }
115
+ }
116
+ // Add nodes with dependencies
117
+ for (const id of sorted.order) {
118
+ const step = this.steps.get(id);
119
+ if (step.dependencies.length > 0) {
120
+ const firstDep = step.dependencies[0];
121
+ const depType = firstDep !== undefined ? step.dependencyTypes?.[firstDep] ?? 'hard' : 'hard';
122
+ graph.addNode(id, {
123
+ dependsOn: step.dependencies,
124
+ type: depType,
125
+ });
126
+ }
127
+ }
128
+ return graph;
129
+ }
130
+ /**
131
+ * Get execution levels for parallel execution
132
+ */
133
+ getExecutionLevels() {
134
+ const nodes = Array.from(this.steps.values()).map((s) => ({
135
+ id: s.id,
136
+ dependencies: s.dependencies,
137
+ }));
138
+ return getExecutionLevels(nodes);
139
+ }
140
+ /**
141
+ * Validate the graph (check for cycles, missing deps)
142
+ */
143
+ validate() {
144
+ const errors = [];
145
+ const nodes = Array.from(this.steps.values()).map((s) => ({
146
+ id: s.id,
147
+ dependencies: s.dependencies,
148
+ }));
149
+ // Check for cycles
150
+ const result = topologicalSort(nodes);
151
+ if (result.hasCycle) {
152
+ errors.push(`Circular dependency detected: ${result.cyclePath?.join(' -> ')}`);
153
+ }
154
+ // Check for missing dependencies
155
+ const stepIds = new Set(this.steps.keys());
156
+ for (const step of this.steps.values()) {
157
+ for (const dep of step.dependencies) {
158
+ if (!stepIds.has(dep)) {
159
+ errors.push(`Step '${step.id}' depends on missing step '${dep}'`);
160
+ }
161
+ }
162
+ }
163
+ return {
164
+ valid: errors.length === 0,
165
+ errors,
166
+ };
167
+ }
168
+ /**
169
+ * Execute the graph with durable semantics
170
+ *
171
+ * @param workflowStep - Cloudflare Workflows step object
172
+ * @param input - Input to pass to all steps
173
+ * @returns Execution result with all step outputs
174
+ */
175
+ async run(workflowStep, input) {
176
+ const validation = this.validate();
177
+ if (!validation.valid) {
178
+ throw new Error(`Invalid execution graph: ${validation.errors.join(', ')}`);
179
+ }
180
+ const levels = this.getExecutionLevels();
181
+ const results = {};
182
+ const stepDurations = {};
183
+ const levelDurations = [];
184
+ const skippedSteps = [];
185
+ const executionOrder = [];
186
+ const completedSteps = [];
187
+ const failedSteps = new Set();
188
+ const startTime = Date.now();
189
+ for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
190
+ const level = levels[levelIndex];
191
+ const levelStartTime = Date.now();
192
+ this.config.progress?.onLevelStart?.(level.level, level.nodes);
193
+ // Filter steps that can run (all hard deps satisfied)
194
+ const runnableSteps = level.nodes.filter((stepId) => {
195
+ const step = this.steps.get(stepId);
196
+ const hardDeps = step.dependencies.filter((dep) => (step.dependencyTypes?.[dep] ?? 'hard') === 'hard');
197
+ return hardDeps.every((dep) => completedSteps.includes(dep));
198
+ });
199
+ // Skip steps whose hard dependencies failed
200
+ const stepsToSkip = level.nodes.filter((stepId) => !runnableSteps.includes(stepId));
201
+ for (const stepId of stepsToSkip) {
202
+ skippedSteps.push(stepId);
203
+ failedSteps.add(stepId);
204
+ }
205
+ // Execute runnable steps in parallel
206
+ const stepPromises = runnableSteps.map(async (stepId) => {
207
+ const step = this.steps.get(stepId);
208
+ const stepStartTime = Date.now();
209
+ this.config.progress?.onStepStart?.(stepId, level.level);
210
+ const context = {
211
+ stepId,
212
+ level: level.level,
213
+ totalLevels: levels.length,
214
+ completedSteps: [...completedSteps],
215
+ attempt: 1,
216
+ };
217
+ try {
218
+ const result = await this.executeStep(workflowStep, step, input, results, context);
219
+ const duration = Date.now() - stepStartTime;
220
+ results[stepId] = result;
221
+ stepDurations[stepId] = duration;
222
+ executionOrder.push(stepId);
223
+ completedSteps.push(stepId);
224
+ this.config.progress?.onStepComplete?.(stepId, level.level, result, duration);
225
+ return { stepId, success: true, result };
226
+ }
227
+ catch (error) {
228
+ const err = error instanceof Error ? error : new Error(String(error));
229
+ const duration = Date.now() - stepStartTime;
230
+ stepDurations[stepId] = duration;
231
+ failedSteps.add(stepId);
232
+ this.config.progress?.onStepError?.(stepId, level.level, err);
233
+ if (!this.config.continueOnError) {
234
+ throw err;
235
+ }
236
+ return { stepId, success: false, error: err };
237
+ }
238
+ });
239
+ // Wait for all steps in this level
240
+ const levelResults = await Promise.all(stepPromises);
241
+ const levelDuration = Date.now() - levelStartTime;
242
+ levelDurations.push(levelDuration);
243
+ this.config.progress?.onLevelComplete?.(level.level, results);
244
+ }
245
+ const totalDuration = Date.now() - startTime;
246
+ // Calculate parallel efficiency
247
+ const sequentialTime = Object.values(stepDurations).reduce((a, b) => a + b, 0);
248
+ const parallelEfficiency = sequentialTime > 0 ? sequentialTime / totalDuration : 1;
249
+ return {
250
+ results,
251
+ metrics: {
252
+ totalDuration,
253
+ levelDurations,
254
+ stepDurations,
255
+ parallelEfficiency,
256
+ },
257
+ skippedSteps,
258
+ executionOrder,
259
+ };
260
+ }
261
+ /**
262
+ * Execute a single step with durability
263
+ */
264
+ async executeStep(workflowStep, step, input, previousResults, context) {
265
+ if (step.config) {
266
+ return workflowStep.do(step.id, step.config, async () => {
267
+ return step.executor(input, previousResults, context);
268
+ });
269
+ }
270
+ else {
271
+ return workflowStep.do(step.id, async () => {
272
+ return step.executor(input, previousResults, context);
273
+ });
274
+ }
275
+ }
276
+ /**
277
+ * Visualize the execution graph as DOT format
278
+ */
279
+ toDot() {
280
+ const graph = this.getGraph();
281
+ return graph.toDot();
282
+ }
283
+ /**
284
+ * Get graph as JSON for debugging
285
+ */
286
+ toJSON() {
287
+ return {
288
+ steps: Array.from(this.steps.values()),
289
+ levels: this.getExecutionLevels(),
290
+ };
291
+ }
292
+ }
293
+ // ============================================================================
294
+ // DurableGraph
295
+ // ============================================================================
296
+ /**
297
+ * DurableGraph - Dependency graph with durable execution support
298
+ *
299
+ * Extends DependencyGraph with execution capabilities using Cloudflare
300
+ * Workflows durability. Provides a higher-level API for building and
301
+ * executing workflow step graphs.
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * const graph = new DurableGraph<{ userId: string }>()
306
+ * .node('fetch-user', async (input) => {
307
+ * return await fetchUser(input.userId)
308
+ * })
309
+ * .node('fetch-orders', ['fetch-user'], async (input, results) => {
310
+ * return await fetchOrders(results['fetch-user'].id)
311
+ * })
312
+ *
313
+ * const result = await graph.execute(step, { userId: '123' })
314
+ * ```
315
+ */
316
+ export class DurableGraph {
317
+ executor;
318
+ nodeMetadata = new Map();
319
+ constructor(config = {}) {
320
+ this.executor = new TopologicalExecutor(config);
321
+ }
322
+ /**
323
+ * Implementation of node() overloads
324
+ */
325
+ node(id, depsOrExecutor, executorOrConfig, maybeConfig) {
326
+ if (typeof depsOrExecutor === 'function') {
327
+ // node(id, executor)
328
+ this.executor.addStep(id, [], depsOrExecutor);
329
+ }
330
+ else if (typeof executorOrConfig === 'function') {
331
+ // node(id, deps, executor) or node(id, deps, executor, config)
332
+ this.executor.addStep(id, depsOrExecutor, executorOrConfig, maybeConfig);
333
+ }
334
+ return this;
335
+ }
336
+ /**
337
+ * Add metadata to a node
338
+ */
339
+ describe(id, metadata) {
340
+ this.nodeMetadata.set(id, metadata);
341
+ return this;
342
+ }
343
+ /**
344
+ * Get execution levels
345
+ */
346
+ levels() {
347
+ return this.executor.getExecutionLevels();
348
+ }
349
+ /**
350
+ * Validate the graph
351
+ */
352
+ validate() {
353
+ return this.executor.validate();
354
+ }
355
+ /**
356
+ * Execute the graph with durable semantics
357
+ */
358
+ async execute(workflowStep, input) {
359
+ return this.executor.run(workflowStep, input);
360
+ }
361
+ /**
362
+ * Get DOT visualization
363
+ */
364
+ toDot() {
365
+ return this.executor.toDot();
366
+ }
367
+ /**
368
+ * Get as JSON
369
+ */
370
+ toJSON() {
371
+ const metadata = {};
372
+ for (const [id, meta] of this.nodeMetadata) {
373
+ metadata[id] = meta;
374
+ }
375
+ return {
376
+ ...this.executor.toJSON(),
377
+ metadata,
378
+ };
379
+ }
380
+ }
381
+ // ============================================================================
382
+ // Convenience functions
383
+ // ============================================================================
384
+ /**
385
+ * Create a new TopologicalExecutor
386
+ */
387
+ export function createExecutor(config) {
388
+ return new TopologicalExecutor(config);
389
+ }
390
+ /**
391
+ * Create a new DurableGraph
392
+ */
393
+ export function createGraph(config) {
394
+ return new DurableGraph(config);
395
+ }
396
+ //# sourceMappingURL=topological-executor.js.map