ai-workflows 2.1.1 → 2.3.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 (211) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -1
  3. package/README.md +305 -184
  4. package/dist/barrier.d.ts +159 -0
  5. package/dist/barrier.d.ts.map +1 -0
  6. package/dist/barrier.js +377 -0
  7. package/dist/barrier.js.map +1 -0
  8. package/dist/cascade-context.d.ts +149 -0
  9. package/dist/cascade-context.d.ts.map +1 -0
  10. package/dist/cascade-context.js +324 -0
  11. package/dist/cascade-context.js.map +1 -0
  12. package/dist/cascade-executor.d.ts +196 -0
  13. package/dist/cascade-executor.d.ts.map +1 -0
  14. package/dist/cascade-executor.js +384 -0
  15. package/dist/cascade-executor.js.map +1 -0
  16. package/dist/context.d.ts.map +1 -1
  17. package/dist/context.js +27 -8
  18. package/dist/context.js.map +1 -1
  19. package/dist/cron-parser.d.ts +65 -0
  20. package/dist/cron-parser.d.ts.map +1 -0
  21. package/dist/cron-parser.js +294 -0
  22. package/dist/cron-parser.js.map +1 -0
  23. package/dist/cron-scheduler.d.ts +117 -0
  24. package/dist/cron-scheduler.d.ts.map +1 -0
  25. package/dist/cron-scheduler.js +176 -0
  26. package/dist/cron-scheduler.js.map +1 -0
  27. package/dist/database-context.d.ts +184 -0
  28. package/dist/database-context.d.ts.map +1 -0
  29. package/dist/database-context.js +428 -0
  30. package/dist/database-context.js.map +1 -0
  31. package/dist/dependency-graph.d.ts +157 -0
  32. package/dist/dependency-graph.d.ts.map +1 -0
  33. package/dist/dependency-graph.js +382 -0
  34. package/dist/dependency-graph.js.map +1 -0
  35. package/dist/digital-objects-adapter.d.ts +159 -0
  36. package/dist/digital-objects-adapter.d.ts.map +1 -0
  37. package/dist/digital-objects-adapter.js +229 -0
  38. package/dist/digital-objects-adapter.js.map +1 -0
  39. package/dist/durable-execution-cloudflare.d.ts +427 -0
  40. package/dist/durable-execution-cloudflare.d.ts.map +1 -0
  41. package/dist/durable-execution-cloudflare.js +510 -0
  42. package/dist/durable-execution-cloudflare.js.map +1 -0
  43. package/dist/durable-execution.d.ts +482 -0
  44. package/dist/durable-execution.d.ts.map +1 -0
  45. package/dist/durable-execution.js +594 -0
  46. package/dist/durable-execution.js.map +1 -0
  47. package/dist/durable-workflow.d.ts +176 -0
  48. package/dist/durable-workflow.d.ts.map +1 -0
  49. package/dist/durable-workflow.js +552 -0
  50. package/dist/durable-workflow.js.map +1 -0
  51. package/dist/every.d.ts +31 -2
  52. package/dist/every.d.ts.map +1 -1
  53. package/dist/every.js +63 -32
  54. package/dist/every.js.map +1 -1
  55. package/dist/graph/index.d.ts +8 -0
  56. package/dist/graph/index.d.ts.map +1 -0
  57. package/dist/graph/index.js +8 -0
  58. package/dist/graph/index.js.map +1 -0
  59. package/dist/graph/topological-sort.d.ts +121 -0
  60. package/dist/graph/topological-sort.d.ts.map +1 -0
  61. package/dist/graph/topological-sort.js +292 -0
  62. package/dist/graph/topological-sort.js.map +1 -0
  63. package/dist/index.d.ts +10 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +25 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/logger.d.ts +101 -0
  68. package/dist/logger.d.ts.map +1 -0
  69. package/dist/logger.js +115 -0
  70. package/dist/logger.js.map +1 -0
  71. package/dist/on.d.ts +35 -10
  72. package/dist/on.d.ts.map +1 -1
  73. package/dist/on.js +53 -19
  74. package/dist/on.js.map +1 -1
  75. package/dist/runtime.d.ts +169 -0
  76. package/dist/runtime.d.ts.map +1 -0
  77. package/dist/runtime.js +275 -0
  78. package/dist/runtime.js.map +1 -0
  79. package/dist/send.d.ts.map +1 -1
  80. package/dist/send.js +4 -3
  81. package/dist/send.js.map +1 -1
  82. package/dist/telemetry.d.ts +150 -0
  83. package/dist/telemetry.d.ts.map +1 -0
  84. package/dist/telemetry.js +388 -0
  85. package/dist/telemetry.js.map +1 -0
  86. package/dist/timer-registry.d.ts +77 -0
  87. package/dist/timer-registry.d.ts.map +1 -0
  88. package/dist/timer-registry.js +154 -0
  89. package/dist/timer-registry.js.map +1 -0
  90. package/dist/types.d.ts +105 -6
  91. package/dist/types.d.ts.map +1 -1
  92. package/dist/types.js +17 -1
  93. package/dist/types.js.map +1 -1
  94. package/dist/worker/durable-step.d.ts +481 -0
  95. package/dist/worker/durable-step.d.ts.map +1 -0
  96. package/dist/worker/durable-step.js +606 -0
  97. package/dist/worker/durable-step.js.map +1 -0
  98. package/dist/worker/index.d.ts +106 -0
  99. package/dist/worker/index.d.ts.map +1 -0
  100. package/dist/worker/index.js +124 -0
  101. package/dist/worker/index.js.map +1 -0
  102. package/dist/worker/state-adapter.d.ts +230 -0
  103. package/dist/worker/state-adapter.d.ts.map +1 -0
  104. package/dist/worker/state-adapter.js +409 -0
  105. package/dist/worker/state-adapter.js.map +1 -0
  106. package/dist/worker/topological-executor.d.ts +282 -0
  107. package/dist/worker/topological-executor.d.ts.map +1 -0
  108. package/dist/worker/topological-executor.js +396 -0
  109. package/dist/worker/topological-executor.js.map +1 -0
  110. package/dist/worker/workflow-builder.d.ts +286 -0
  111. package/dist/worker/workflow-builder.d.ts.map +1 -0
  112. package/dist/worker/workflow-builder.js +565 -0
  113. package/dist/worker/workflow-builder.js.map +1 -0
  114. package/dist/worker.d.ts +800 -0
  115. package/dist/worker.d.ts.map +1 -0
  116. package/dist/worker.js +2428 -0
  117. package/dist/worker.js.map +1 -0
  118. package/dist/workflow-builder.d.ts +287 -0
  119. package/dist/workflow-builder.d.ts.map +1 -0
  120. package/dist/workflow-builder.js +762 -0
  121. package/dist/workflow-builder.js.map +1 -0
  122. package/dist/workflow.d.ts +14 -30
  123. package/dist/workflow.d.ts.map +1 -1
  124. package/dist/workflow.js +136 -292
  125. package/dist/workflow.js.map +1 -1
  126. package/examples/01-ecommerce-order-pipeline.ts +358 -0
  127. package/examples/02-content-moderation-cascade.ts +454 -0
  128. package/examples/03-scheduled-reporting-dependencies.ts +479 -0
  129. package/examples/04-database-persistence.ts +518 -0
  130. package/examples/README.md +173 -0
  131. package/package.json +21 -4
  132. package/src/__tests__/digital-objects-adapter.test.ts +274 -0
  133. package/src/__tests__/durable-workflow.test.ts +297 -0
  134. package/src/barrier.ts +507 -0
  135. package/src/cascade-context.ts +495 -0
  136. package/src/cascade-executor.ts +588 -0
  137. package/src/context.ts +51 -17
  138. package/src/cron-parser.ts +347 -0
  139. package/src/cron-scheduler.ts +239 -0
  140. package/src/database-context.ts +658 -0
  141. package/src/dependency-graph.ts +518 -0
  142. package/src/digital-objects-adapter.ts +351 -0
  143. package/src/durable-execution-cloudflare.ts +855 -0
  144. package/src/durable-execution.ts +1042 -0
  145. package/src/durable-workflow.ts +717 -0
  146. package/src/every.ts +104 -35
  147. package/src/graph/index.ts +19 -0
  148. package/src/graph/topological-sort.ts +412 -0
  149. package/src/index.ts +147 -0
  150. package/src/logger.ts +148 -0
  151. package/src/on.ts +81 -26
  152. package/src/runtime.ts +436 -0
  153. package/src/send.ts +4 -5
  154. package/src/telemetry.ts +577 -0
  155. package/src/timer-registry.ts +179 -0
  156. package/src/types.ts +146 -10
  157. package/src/worker/durable-step.ts +976 -0
  158. package/src/worker/index.ts +216 -0
  159. package/src/worker/state-adapter.ts +589 -0
  160. package/src/worker/topological-executor.ts +625 -0
  161. package/src/worker/workflow-builder.ts +871 -0
  162. package/src/worker.ts +2906 -0
  163. package/src/workflow-builder.ts +1068 -0
  164. package/src/workflow.ts +199 -355
  165. package/test/barrier-join.test.ts +442 -0
  166. package/test/barrier-unhandled-rejections.test.ts +359 -0
  167. package/test/cascade-context.test.ts +390 -0
  168. package/test/cascade-executor.test.ts +852 -0
  169. package/test/cron-parser.test.ts +314 -0
  170. package/test/cron-scheduler.test.ts +291 -0
  171. package/test/database-context.test.ts +770 -0
  172. package/test/db-provider-adapter.test.ts +862 -0
  173. package/test/dependency-graph.test.ts +512 -0
  174. package/test/durable-execution-cloudflare.test.ts +606 -0
  175. package/test/durable-execution-in-process.test.ts +286 -0
  176. package/test/durable-execution.test.ts +247 -0
  177. package/test/e2e/workflow-scenarios.e2e.test.ts +1039 -0
  178. package/test/graph/topological-sort.test.ts +586 -0
  179. package/test/integration.test.ts +442 -0
  180. package/test/rpc-surface.test.ts +946 -0
  181. package/test/runtime.test.ts +262 -0
  182. package/test/schedule-timer-cleanup.test.ts +353 -0
  183. package/test/send-race-conditions.test.ts +400 -0
  184. package/test/type-safety-every.test.ts +303 -0
  185. package/test/worker/durable-cascade.test.ts +1117 -0
  186. package/test/worker/durable-step.test.ts +723 -0
  187. package/test/worker/topological-executor.test.ts +1240 -0
  188. package/test/worker/workflow-builder.test.ts +1067 -0
  189. package/test/worker.test.ts +608 -0
  190. package/test/workflow-builder.test.ts +1670 -0
  191. package/test/workflow-cron.test.ts +256 -0
  192. package/test/workflow-state-adapter.test.ts +923 -0
  193. package/test/workflow.test.ts +25 -22
  194. package/tsconfig.json +3 -1
  195. package/vitest.config.ts +38 -1
  196. package/vitest.workers.config.ts +44 -0
  197. package/wrangler.jsonc +22 -0
  198. package/.turbo/turbo-test.log +0 -7
  199. package/src/context.js +0 -83
  200. package/src/every.js +0 -267
  201. package/src/index.js +0 -71
  202. package/src/on.js +0 -79
  203. package/src/send.js +0 -111
  204. package/src/types.js +0 -4
  205. package/src/workflow.js +0 -455
  206. package/test/context.test.js +0 -116
  207. package/test/every.test.js +0 -282
  208. package/test/on.test.js +0 -80
  209. package/test/send.test.js +0 -89
  210. package/test/workflow.test.js +0 -224
  211. package/vitest.config.js +0 -7
@@ -0,0 +1,351 @@
1
+ /**
2
+ * digital-objects adapter for ai-workflows
3
+ *
4
+ * Provides durable persistence for workflow state using digital-objects.
5
+ * Maps workflow concepts to digital-objects primitives:
6
+ * - Workflows -> Things
7
+ * - Workflow steps/events -> Actions
8
+ * - State changes -> Actions with data
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createMemoryProvider } from 'digital-objects'
13
+ * import { createDigitalObjectsAdapter } from 'ai-workflows'
14
+ *
15
+ * const provider = createMemoryProvider()
16
+ * const db = createDigitalObjectsAdapter(provider)
17
+ *
18
+ * const workflow = Workflow($ => {
19
+ * $.on.Customer.created(async (customer, $) => {
20
+ * await $.send('Email.welcome', { to: customer.email })
21
+ * })
22
+ * }, { db })
23
+ * ```
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+
28
+ import type { DigitalObjectsProvider, Thing, Action, ActionStatusType } from 'digital-objects'
29
+ import type { DatabaseContext, ActionData, ArtifactData } from './types.js'
30
+
31
+ /**
32
+ * Workflow instance data stored as a Thing
33
+ */
34
+ export interface WorkflowThingData {
35
+ name: string
36
+ status: 'initializing' | 'running' | 'paused' | 'completed' | 'failed'
37
+ context: Record<string, unknown>
38
+ registeredEvents: string[]
39
+ registeredSchedules: string[]
40
+ }
41
+
42
+ /**
43
+ * Event data stored in Actions
44
+ */
45
+ export interface EventActionData {
46
+ event: string
47
+ data: unknown
48
+ eventId: string
49
+ }
50
+
51
+ /**
52
+ * Step/action data stored in Actions
53
+ */
54
+ export interface StepActionData {
55
+ stepName: string
56
+ status: ActionStatusType
57
+ tier?: 'code' | 'generative' | 'agentic' | 'human'
58
+ input?: unknown
59
+ output?: unknown
60
+ error?: string
61
+ metadata?: Record<string, unknown>
62
+ }
63
+
64
+ /**
65
+ * Artifact data stored as a Thing
66
+ */
67
+ export interface ArtifactThingData extends ArtifactData {
68
+ workflowId?: string
69
+ }
70
+
71
+ /**
72
+ * Options for creating the digital-objects adapter
73
+ */
74
+ export interface DigitalObjectsAdapterOptions {
75
+ /**
76
+ * Workflow instance ID for tracking actions
77
+ * If provided, all actions will be associated with this workflow
78
+ */
79
+ workflowId?: string
80
+
81
+ /**
82
+ * Whether to auto-define required nouns and verbs
83
+ * @default true
84
+ */
85
+ autoDefine?: boolean
86
+ }
87
+
88
+ /**
89
+ * Extended DatabaseContext with digital-objects specific methods
90
+ */
91
+ export interface DigitalObjectsDatabaseContext extends DatabaseContext {
92
+ /**
93
+ * The underlying digital-objects provider
94
+ */
95
+ readonly provider: DigitalObjectsProvider
96
+
97
+ /**
98
+ * The workflow instance ID (if set)
99
+ */
100
+ readonly workflowId?: string
101
+
102
+ /**
103
+ * Get workflow state as a Thing
104
+ */
105
+ getWorkflow<T = WorkflowThingData>(id: string): Promise<Thing<T> | null>
106
+
107
+ /**
108
+ * Update workflow state
109
+ */
110
+ updateWorkflow<T = WorkflowThingData>(id: string, data: Partial<T>): Promise<Thing<T>>
111
+
112
+ /**
113
+ * List all actions for a workflow
114
+ */
115
+ listWorkflowActions<T = unknown>(workflowId: string): Promise<Action<T>[]>
116
+
117
+ /**
118
+ * Query actions by verb
119
+ */
120
+ listActionsByVerb<T = unknown>(verb: string): Promise<Action<T>[]>
121
+ }
122
+
123
+ /**
124
+ * Define workflow-specific nouns in the provider
125
+ */
126
+ async function defineWorkflowNouns(provider: DigitalObjectsProvider): Promise<void> {
127
+ // Check if already defined
128
+ const existing = await provider.getNoun('Workflow')
129
+ if (existing) return
130
+
131
+ await Promise.all([
132
+ provider.defineNoun({
133
+ name: 'Workflow',
134
+ description: 'Workflow instance with state and event handlers',
135
+ }),
136
+ provider.defineNoun({
137
+ name: 'Artifact',
138
+ description: 'Cached workflow artifact (AST, compiled code, etc.)',
139
+ }),
140
+ provider.defineNoun({
141
+ name: 'Schedule',
142
+ description: 'Scheduled task registration',
143
+ }),
144
+ provider.defineNoun({
145
+ name: 'Cascade',
146
+ description: 'Cascade execution context for tiered processing',
147
+ }),
148
+ ])
149
+ }
150
+
151
+ /**
152
+ * Define workflow-specific verbs in the provider
153
+ */
154
+ async function defineWorkflowVerbs(provider: DigitalObjectsProvider): Promise<void> {
155
+ // Check if already defined
156
+ const existing = await provider.getVerb('emit')
157
+ if (existing) return
158
+
159
+ await Promise.all([
160
+ provider.defineVerb({
161
+ name: 'emit',
162
+ description: 'Emit an event',
163
+ }),
164
+ provider.defineVerb({
165
+ name: 'track',
166
+ description: 'Track a telemetry event (fire and forget)',
167
+ }),
168
+ provider.defineVerb({
169
+ name: 'execute',
170
+ description: 'Execute a workflow step',
171
+ }),
172
+ provider.defineVerb({
173
+ name: 'complete',
174
+ description: 'Mark an action as completed',
175
+ }),
176
+ provider.defineVerb({
177
+ name: 'fail',
178
+ description: 'Mark an action as failed',
179
+ }),
180
+ provider.defineVerb({
181
+ name: 'set',
182
+ description: 'Set a context value',
183
+ }),
184
+ provider.defineVerb({
185
+ name: 'step',
186
+ description: 'Record a cascade step',
187
+ }),
188
+ provider.defineVerb({
189
+ name: 'dependsOn',
190
+ description: 'Declare step dependency',
191
+ }),
192
+ provider.defineVerb({
193
+ name: 'escalate',
194
+ description: 'Escalate to next tier in cascade',
195
+ }),
196
+ provider.defineVerb({
197
+ name: 'transition',
198
+ description: 'Workflow state transition',
199
+ }),
200
+ ])
201
+ }
202
+
203
+ /**
204
+ * Create a DatabaseContext adapter backed by digital-objects
205
+ *
206
+ * @param provider - The digital-objects provider (MemoryProvider, NS, etc.)
207
+ * @param options - Optional configuration
208
+ * @returns A DatabaseContext compatible with ai-workflows
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * import { createMemoryProvider } from 'digital-objects'
213
+ * import { createDigitalObjectsAdapter, Workflow } from 'ai-workflows'
214
+ *
215
+ * const provider = createMemoryProvider()
216
+ * const db = await createDigitalObjectsAdapter(provider)
217
+ *
218
+ * // Use with Workflow
219
+ * const workflow = Workflow($ => {
220
+ * $.on.Order.created(async (order, $) => {
221
+ * $.log('Order created:', order.id)
222
+ * })
223
+ * }, { db })
224
+ * ```
225
+ */
226
+ export async function createDigitalObjectsAdapter(
227
+ provider: DigitalObjectsProvider,
228
+ options: DigitalObjectsAdapterOptions = {}
229
+ ): Promise<DigitalObjectsDatabaseContext> {
230
+ const { workflowId, autoDefine = true } = options
231
+
232
+ // Auto-define nouns and verbs if enabled
233
+ if (autoDefine) {
234
+ await defineWorkflowNouns(provider)
235
+ await defineWorkflowVerbs(provider)
236
+ }
237
+
238
+ const result: DigitalObjectsDatabaseContext = {
239
+ provider,
240
+ ...(workflowId !== undefined && { workflowId }),
241
+
242
+ async recordEvent(event: string, data: unknown): Promise<void> {
243
+ const eventId = crypto.randomUUID()
244
+ await provider.perform<EventActionData>('emit', workflowId, undefined, {
245
+ event,
246
+ data,
247
+ eventId,
248
+ })
249
+ },
250
+
251
+ async createAction(action: ActionData): Promise<void> {
252
+ const stepData: StepActionData = {
253
+ stepName: `${action.actor}.${action.action}.${action.object}`,
254
+ status: (action.status as ActionStatusType) ?? 'pending',
255
+ }
256
+ if (action.metadata) {
257
+ stepData.metadata = action.metadata
258
+ }
259
+ await provider.perform<StepActionData>(action.action, action.actor, action.object, stepData)
260
+ },
261
+
262
+ async completeAction(id: string, result: unknown): Promise<void> {
263
+ // Record completion as an action pointing to the original action
264
+ await provider.perform<{ result: unknown; completedAt: number }>('complete', undefined, id, {
265
+ result,
266
+ completedAt: Date.now(),
267
+ })
268
+ },
269
+
270
+ async storeArtifact(artifact: ArtifactData): Promise<void> {
271
+ const artifactData: ArtifactThingData = {
272
+ ...artifact,
273
+ ...(workflowId && { workflowId }),
274
+ }
275
+ // Use artifact.key as the ID for easy retrieval
276
+ await provider.create<ArtifactThingData>('Artifact', artifactData, artifact.key)
277
+ },
278
+
279
+ async getArtifact(key: string): Promise<unknown | null> {
280
+ const thing = await provider.get<ArtifactThingData>(key)
281
+ return thing?.data?.content ?? null
282
+ },
283
+
284
+ // Extended methods for digital-objects integration
285
+
286
+ async getWorkflow<T = WorkflowThingData>(id: string): Promise<Thing<T> | null> {
287
+ return provider.get<T>(id)
288
+ },
289
+
290
+ async updateWorkflow<T = WorkflowThingData>(id: string, data: Partial<T>): Promise<Thing<T>> {
291
+ return provider.update<T>(id, data)
292
+ },
293
+
294
+ async listWorkflowActions<T = unknown>(wfId: string): Promise<Action<T>[]> {
295
+ return provider.listActions<T>({ subject: wfId })
296
+ },
297
+
298
+ async listActionsByVerb<T = unknown>(verb: string): Promise<Action<T>[]> {
299
+ return provider.listActions<T>({ verb })
300
+ },
301
+ }
302
+
303
+ return result
304
+ }
305
+
306
+ /**
307
+ * Synchronous factory that returns a partially initialized adapter.
308
+ * Use this when you need synchronous construction but can defer initialization.
309
+ *
310
+ * @param provider - The digital-objects provider
311
+ * @param options - Optional configuration
312
+ * @returns A function that initializes and returns the adapter
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * const initAdapter = createDigitalObjectsAdapterSync(provider)
317
+ *
318
+ * // Later, when async is available
319
+ * const db = await initAdapter()
320
+ * ```
321
+ */
322
+ export function createDigitalObjectsAdapterSync(
323
+ provider: DigitalObjectsProvider,
324
+ options: DigitalObjectsAdapterOptions = {}
325
+ ): () => Promise<DigitalObjectsDatabaseContext> {
326
+ return () => createDigitalObjectsAdapter(provider, options)
327
+ }
328
+
329
+ /**
330
+ * Create a simple DatabaseContext without the extended digital-objects methods.
331
+ * This is useful when you only need the basic DatabaseContext interface.
332
+ *
333
+ * @param provider - The digital-objects provider
334
+ * @param options - Optional configuration
335
+ * @returns A basic DatabaseContext
336
+ */
337
+ export async function createSimpleAdapter(
338
+ provider: DigitalObjectsProvider,
339
+ options: DigitalObjectsAdapterOptions = {}
340
+ ): Promise<DatabaseContext> {
341
+ const fullAdapter = await createDigitalObjectsAdapter(provider, options)
342
+
343
+ // Return only the DatabaseContext interface
344
+ return {
345
+ recordEvent: fullAdapter.recordEvent.bind(fullAdapter),
346
+ createAction: fullAdapter.createAction.bind(fullAdapter),
347
+ completeAction: fullAdapter.completeAction.bind(fullAdapter),
348
+ storeArtifact: fullAdapter.storeArtifact.bind(fullAdapter),
349
+ getArtifact: fullAdapter.getArtifact.bind(fullAdapter),
350
+ }
351
+ }