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
@@ -86,9 +86,7 @@ export class MissingNodeError extends Error {
86
86
  referencedBy: string
87
87
 
88
88
  constructor(missingNode: string, referencedBy: string) {
89
- super(
90
- `Missing dependency '${missingNode}' referenced by '${referencedBy}'`
91
- )
89
+ super(`Missing dependency '${missingNode}' referenced by '${referencedBy}'`)
92
90
  this.name = 'MissingNodeError'
93
91
  this.missingNode = missingNode
94
92
  this.referencedBy = referencedBy
@@ -168,9 +166,7 @@ export function topologicalSortKahn(
168
166
 
169
167
  // Start with nodes that have no dependencies (in-degree 0)
170
168
  // Sort alphabetically for determinism
171
- const queue: string[] = [...nodeSet]
172
- .filter((id) => inDegreesCopy.get(id) === 0)
173
- .sort()
169
+ const queue: string[] = [...nodeSet].filter((id) => inDegreesCopy.get(id) === 0).sort()
174
170
 
175
171
  while (queue.length > 0) {
176
172
  // Sort queue for deterministic ordering
@@ -293,7 +289,7 @@ export function topologicalSortDFS(
293
289
  return {
294
290
  order: hasCycle ? order : order, // DFS produces correct order
295
291
  hasCycle,
296
- cyclePath,
292
+ ...(cyclePath !== undefined && { cyclePath }),
297
293
  }
298
294
  }
299
295
 
@@ -318,7 +314,9 @@ export function topologicalSort(
318
314
  // Kahn's algorithm doesn't provide cycle path, so detect it separately
319
315
  if (result.hasCycle) {
320
316
  const dfsResult = topologicalSortDFS(nodes, options)
321
- result.cyclePath = dfsResult.cyclePath
317
+ if (dfsResult.cyclePath !== undefined) {
318
+ result = { ...result, cyclePath: dfsResult.cyclePath }
319
+ }
322
320
  }
323
321
  } else {
324
322
  result = topologicalSortDFS(nodes, options)
package/src/index.ts CHANGED
@@ -63,6 +63,18 @@
63
63
  // Main Workflow API
64
64
  export { Workflow, createTestContext, parseEvent, type WorkflowInstance } from './workflow.js'
65
65
 
66
+ // WorkflowRuntime - single owner of the `$` runtime contract
67
+ // (composes cascade-context, database-context, on/send/every internally).
68
+ // New callers should reach for createWorkflowRuntime() to understand exactly
69
+ // what `$` contains end-to-end. The Workflow() function wraps a runtime with
70
+ // schedule/timer lifecycle; for tests, runtime.dispatch() is the canonical
71
+ // surface.
72
+ export {
73
+ createWorkflowRuntime,
74
+ type WorkflowRuntime,
75
+ type WorkflowRuntimeOptions,
76
+ } from './runtime.js'
77
+
66
78
  // Standalone event handling (for global registration)
67
79
  export { on, registerEventHandler, getEventHandlers, clearEventHandlers } from './on.js'
68
80
 
@@ -160,6 +172,63 @@ export {
160
172
  type SkipCondition,
161
173
  } from './cascade-executor.js'
162
174
 
175
+ // DurableStep - Cloudflare Workflows step wrapper
176
+ export {
177
+ DurableStep,
178
+ StepContext as DurableStepContext,
179
+ type StepConfig,
180
+ type StepMetadata,
181
+ type RetryConfig as DurableRetryConfig,
182
+ type WorkflowStep,
183
+ type StepFunction as DurableStepFunction,
184
+ // Cascade types
185
+ DurableCascadeStep,
186
+ AllTiersFailed,
187
+ CascadeTimeout,
188
+ DEFAULT_CASCADE_TIMEOUTS,
189
+ CASCADE_TIER_ORDER,
190
+ type CascadeTier,
191
+ type CascadeConfig as DurableCascadeConfig,
192
+ type CascadeTierConfig,
193
+ type CascadeTierResult,
194
+ type CascadeResult as DurableCascadeResult,
195
+ type CascadeContext as DurableCascadeContext,
196
+ type CascadeTierContext,
197
+ type AiBinding,
198
+ type HumanReviewRequest,
199
+ type CodeTierHandler,
200
+ type AiTierHandler,
201
+ type HumanTierHandler,
202
+ } from './worker/durable-step.js'
203
+
204
+ // WorkflowBuilder DSL - Fluent API for building durable workflows
205
+ export {
206
+ workflow,
207
+ WorkflowBuilder,
208
+ type StepDefinition,
209
+ type StepChain,
210
+ type ConditionalChain,
211
+ type LoopChain,
212
+ type BuiltWorkflow,
213
+ type StepContext,
214
+ type RetryConfig,
215
+ type StepFunction,
216
+ type ConditionFunction,
217
+ type ErrorHandler,
218
+ type LoopOptions,
219
+ type ForEachOptions,
220
+ } from './workflow-builder.js'
221
+
222
+ // WorkflowStateAdapter - Persistent state storage
223
+ export {
224
+ WorkflowStateAdapter,
225
+ type PersistedWorkflowState,
226
+ type StepCheckpoint,
227
+ type WorkflowHistoryEntry as StateHistoryEntry,
228
+ type SnapshotInfo,
229
+ type DatabaseConnection,
230
+ } from './worker/state-adapter.js'
231
+
163
232
  // Types
164
233
  export type {
165
234
  EventHandler,
package/src/logger.ts ADDED
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Configurable Logger Interface
3
+ *
4
+ * Provides a pluggable logging abstraction for ai-workflows.
5
+ * By default uses console.log/warn/error, but can be replaced
6
+ * with any logging implementation.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { setLogger, getLogger } from 'ai-workflows'
11
+ *
12
+ * // Use default console logger
13
+ * const logger = getLogger()
14
+ * logger.log('[workflow] Starting', { workflowId: '123' })
15
+ *
16
+ * // Set a custom logger
17
+ * setLogger({
18
+ * log: (msg, data) => winston.info(msg, data),
19
+ * warn: (msg, data) => winston.warn(msg, data),
20
+ * error: (msg, error) => winston.error(msg, { error }),
21
+ * })
22
+ *
23
+ * // Disable logging
24
+ * setLogger({
25
+ * log: () => {},
26
+ * warn: () => {},
27
+ * error: () => {},
28
+ * })
29
+ * ```
30
+ */
31
+
32
+ /**
33
+ * Logger interface for ai-workflows
34
+ *
35
+ * Implementations must provide log, warn, and error methods.
36
+ * Data parameter is optional and can be any value for context.
37
+ */
38
+ export interface Logger {
39
+ /**
40
+ * Log informational messages
41
+ * @param msg - The log message
42
+ * @param data - Optional data for context
43
+ */
44
+ log(msg: string, data?: unknown): void
45
+
46
+ /**
47
+ * Log warning messages
48
+ * @param msg - The warning message
49
+ * @param data - Optional data for context
50
+ */
51
+ warn(msg: string, data?: unknown): void
52
+
53
+ /**
54
+ * Log error messages
55
+ * @param msg - The error message
56
+ * @param error - Optional error object or data
57
+ */
58
+ error(msg: string, error?: unknown): void
59
+ }
60
+
61
+ /**
62
+ * Default console logger implementation
63
+ *
64
+ * Uses console.log, console.warn, and console.error.
65
+ */
66
+ export const consoleLogger: Logger = {
67
+ log(msg: string, data?: unknown): void {
68
+ if (data !== undefined) {
69
+ console.log(msg, data)
70
+ } else {
71
+ console.log(msg)
72
+ }
73
+ },
74
+
75
+ warn(msg: string, data?: unknown): void {
76
+ if (data !== undefined) {
77
+ console.warn(msg, data)
78
+ } else {
79
+ console.warn(msg)
80
+ }
81
+ },
82
+
83
+ error(msg: string, error?: unknown): void {
84
+ if (error !== undefined) {
85
+ console.error(msg, error)
86
+ } else {
87
+ console.error(msg)
88
+ }
89
+ },
90
+ }
91
+
92
+ /**
93
+ * No-op logger that discards all messages
94
+ *
95
+ * Useful for silencing logs in tests or production.
96
+ */
97
+ export const noopLogger: Logger = {
98
+ log(): void {},
99
+ warn(): void {},
100
+ error(): void {},
101
+ }
102
+
103
+ /**
104
+ * Current logger instance
105
+ * Defaults to consoleLogger
106
+ */
107
+ let currentLogger: Logger = consoleLogger
108
+
109
+ /**
110
+ * Get the current logger instance
111
+ *
112
+ * @returns The current Logger implementation
113
+ */
114
+ export function getLogger(): Logger {
115
+ return currentLogger
116
+ }
117
+
118
+ /**
119
+ * Set the logger implementation
120
+ *
121
+ * @param logger - The Logger implementation to use
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * // Use a custom logger
126
+ * setLogger({
127
+ * log: (msg, data) => myLogger.info(msg, data),
128
+ * warn: (msg, data) => myLogger.warn(msg, data),
129
+ * error: (msg, error) => myLogger.error(msg, { error }),
130
+ * })
131
+ *
132
+ * // Disable logging
133
+ * setLogger(noopLogger)
134
+ *
135
+ * // Reset to console
136
+ * setLogger(consoleLogger)
137
+ * ```
138
+ */
139
+ export function setLogger(logger: Logger): void {
140
+ currentLogger = logger
141
+ }
142
+
143
+ /**
144
+ * Reset the logger to the default console logger
145
+ */
146
+ export function resetLogger(): void {
147
+ currentLogger = consoleLogger
148
+ }
package/src/on.ts CHANGED
@@ -54,7 +54,7 @@ export function registerEventHandler(
54
54
  event,
55
55
  handler,
56
56
  source: handler.toString(),
57
- dependencies,
57
+ ...(dependencies !== undefined && { dependencies }),
58
58
  })
59
59
  }
60
60
 
@@ -105,20 +105,19 @@ export function createTypedOnProxy(registerCallback: OnProxyRegistrationCallback
105
105
  return (handler: EventHandler, dependencies?: DependencyConfig) => {
106
106
  registerCallback(noun, event, handler, dependencies)
107
107
  }
108
- }
108
+ },
109
109
  })
110
110
 
111
111
  // Create typed handler for the top-level proxy (noun accessors)
112
112
  const onHandler: OnProxyHandler = {
113
- get(
114
- _target: Record<string, NounEventProxy>,
115
- noun: string,
116
- _receiver: unknown
117
- ): NounEventProxy {
113
+ get(_target: Record<string, NounEventProxy>, noun: string, _receiver: unknown): NounEventProxy {
118
114
  // Return a proxy for the event level with typed handler
119
- const eventTarget: Record<string, (handler: EventHandler, dependencies?: DependencyConfig) => void> = {}
115
+ const eventTarget: Record<
116
+ string,
117
+ (handler: EventHandler, dependencies?: DependencyConfig) => void
118
+ > = {}
120
119
  return new Proxy(eventTarget, createNounHandler(noun)) as NounEventProxy
121
- }
120
+ },
122
121
  }
123
122
 
124
123
  // Create and return the typed OnProxy