ai-workflows 2.1.3 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +8 -1
- package/README.md +2 -0
- package/dist/barrier.d.ts +6 -0
- package/dist/barrier.d.ts.map +1 -1
- package/dist/barrier.js +45 -7
- package/dist/barrier.js.map +1 -1
- package/dist/cascade-context.d.ts.map +1 -1
- package/dist/cascade-context.js +25 -25
- package/dist/cascade-context.js.map +1 -1
- package/dist/cascade-executor.d.ts.map +1 -1
- package/dist/cascade-executor.js +1 -1
- package/dist/cascade-executor.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +23 -7
- package/dist/context.js.map +1 -1
- package/dist/cron-parser.d.ts +65 -0
- package/dist/cron-parser.d.ts.map +1 -0
- package/dist/cron-parser.js +294 -0
- package/dist/cron-parser.js.map +1 -0
- package/dist/cron-scheduler.d.ts +117 -0
- package/dist/cron-scheduler.d.ts.map +1 -0
- package/dist/cron-scheduler.js +176 -0
- package/dist/cron-scheduler.js.map +1 -0
- package/dist/database-context.d.ts +184 -0
- package/dist/database-context.d.ts.map +1 -0
- package/dist/database-context.js +428 -0
- package/dist/database-context.js.map +1 -0
- package/dist/digital-objects-adapter.d.ts +159 -0
- package/dist/digital-objects-adapter.d.ts.map +1 -0
- package/dist/digital-objects-adapter.js +229 -0
- package/dist/digital-objects-adapter.js.map +1 -0
- package/dist/durable-execution-cloudflare.d.ts +427 -0
- package/dist/durable-execution-cloudflare.d.ts.map +1 -0
- package/dist/durable-execution-cloudflare.js +510 -0
- package/dist/durable-execution-cloudflare.js.map +1 -0
- package/dist/durable-execution.d.ts +482 -0
- package/dist/durable-execution.d.ts.map +1 -0
- package/dist/durable-execution.js +594 -0
- package/dist/durable-execution.js.map +1 -0
- package/dist/durable-workflow.d.ts +176 -0
- package/dist/durable-workflow.d.ts.map +1 -0
- package/dist/durable-workflow.js +552 -0
- package/dist/durable-workflow.js.map +1 -0
- package/dist/graph/topological-sort.d.ts.map +1 -1
- package/dist/graph/topological-sort.js +5 -5
- package/dist/graph/topological-sort.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +101 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +115 -0
- package/dist/logger.js.map +1 -0
- package/dist/on.d.ts.map +1 -1
- package/dist/on.js +3 -3
- package/dist/on.js.map +1 -1
- package/dist/runtime.d.ts +169 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +275 -0
- package/dist/runtime.js.map +1 -0
- package/dist/send.d.ts.map +1 -1
- package/dist/send.js +4 -3
- package/dist/send.js.map +1 -1
- package/dist/telemetry.d.ts +150 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +388 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/timer-registry.d.ts +25 -0
- package/dist/timer-registry.d.ts.map +1 -1
- package/dist/timer-registry.js +42 -8
- package/dist/timer-registry.js.map +1 -1
- package/dist/types.d.ts +17 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/worker/durable-step.d.ts +481 -0
- package/dist/worker/durable-step.d.ts.map +1 -0
- package/dist/worker/durable-step.js +606 -0
- package/dist/worker/durable-step.js.map +1 -0
- package/dist/worker/index.d.ts +106 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +124 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/state-adapter.d.ts +230 -0
- package/dist/worker/state-adapter.d.ts.map +1 -0
- package/dist/worker/state-adapter.js +409 -0
- package/dist/worker/state-adapter.js.map +1 -0
- package/dist/worker/topological-executor.d.ts +282 -0
- package/dist/worker/topological-executor.d.ts.map +1 -0
- package/dist/worker/topological-executor.js +396 -0
- package/dist/worker/topological-executor.js.map +1 -0
- package/dist/worker/workflow-builder.d.ts +286 -0
- package/dist/worker/workflow-builder.d.ts.map +1 -0
- package/dist/worker/workflow-builder.js +565 -0
- package/dist/worker/workflow-builder.js.map +1 -0
- package/dist/worker.d.ts +800 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +2428 -0
- package/dist/worker.js.map +1 -0
- package/dist/workflow-builder.d.ts +287 -0
- package/dist/workflow-builder.d.ts.map +1 -0
- package/dist/workflow-builder.js +762 -0
- package/dist/workflow-builder.js.map +1 -0
- package/dist/workflow.d.ts +14 -30
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +132 -292
- package/dist/workflow.js.map +1 -1
- package/examples/01-ecommerce-order-pipeline.ts +358 -0
- package/examples/02-content-moderation-cascade.ts +454 -0
- package/examples/03-scheduled-reporting-dependencies.ts +479 -0
- package/examples/04-database-persistence.ts +518 -0
- package/examples/README.md +173 -0
- package/package.json +30 -13
- package/src/__tests__/digital-objects-adapter.test.ts +274 -0
- package/src/__tests__/durable-workflow.test.ts +297 -0
- package/src/barrier.ts +48 -7
- package/src/cascade-context.ts +36 -29
- package/src/cascade-executor.ts +3 -2
- package/src/context.ts +41 -12
- package/src/cron-parser.ts +347 -0
- package/src/cron-scheduler.ts +239 -0
- package/src/database-context.ts +658 -0
- package/src/digital-objects-adapter.ts +351 -0
- package/src/durable-execution-cloudflare.ts +855 -0
- package/src/durable-execution.ts +1042 -0
- package/src/durable-workflow.ts +717 -0
- package/src/graph/topological-sort.ts +6 -8
- package/src/index.ts +69 -0
- package/src/logger.ts +148 -0
- package/src/on.ts +8 -9
- package/src/runtime.ts +436 -0
- package/src/send.ts +4 -5
- package/src/telemetry.ts +577 -0
- package/src/timer-registry.ts +44 -10
- package/src/types.ts +32 -17
- package/src/worker/durable-step.ts +976 -0
- package/src/worker/index.ts +216 -0
- package/src/worker/state-adapter.ts +589 -0
- package/src/worker/topological-executor.ts +625 -0
- package/src/worker/workflow-builder.ts +871 -0
- package/src/worker.ts +2906 -0
- package/src/workflow-builder.ts +1068 -0
- package/src/workflow.ts +188 -351
- package/test/barrier-join.test.ts +32 -24
- package/test/cascade-executor.test.ts +9 -16
- package/test/cron-parser.test.ts +314 -0
- package/test/cron-scheduler.test.ts +291 -0
- package/test/database-context.test.ts +770 -0
- package/test/db-provider-adapter.test.ts +862 -0
- package/test/durable-execution-cloudflare.test.ts +606 -0
- package/test/durable-execution-in-process.test.ts +286 -0
- package/test/durable-execution.test.ts +247 -0
- package/test/e2e/workflow-scenarios.e2e.test.ts +1039 -0
- package/test/integration.test.ts +442 -0
- package/test/rpc-surface.test.ts +946 -0
- package/test/runtime.test.ts +262 -0
- package/test/schedule-timer-cleanup.test.ts +30 -21
- package/test/send-race-conditions.test.ts +30 -40
- package/test/worker/durable-cascade.test.ts +1117 -0
- package/test/worker/durable-step.test.ts +723 -0
- package/test/worker/topological-executor.test.ts +1240 -0
- package/test/worker/workflow-builder.test.ts +1067 -0
- package/test/worker.test.ts +608 -0
- package/test/workflow-builder.test.ts +1670 -0
- package/test/workflow-cron.test.ts +256 -0
- package/test/workflow-state-adapter.test.ts +923 -0
- package/test/workflow.test.ts +25 -22
- package/tsconfig.json +3 -1
- package/vitest.config.ts +38 -1
- package/vitest.workers.config.ts +44 -0
- package/wrangler.jsonc +22 -0
- package/.turbo/turbo-test.log +0 -169
- package/LICENSE +0 -21
- package/src/context.js +0 -83
- package/src/every.js +0 -267
- package/src/index.js +0 -71
- package/src/on.js +0 -79
- package/src/send.js +0 -111
- package/src/types.js +0 -4
- package/src/workflow.js +0 -455
- package/test/context.test.js +0 -116
- package/test/every.test.js +0 -282
- package/test/on.test.js +0 -80
- package/test/send.test.js +0 -89
- package/test/workflow.test.js +0 -224
- package/vitest.config.js +0 -7
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
|
2
|
+
import { Workflow } from '../src/workflow.js'
|
|
3
|
+
import { clearEventHandlers } from '../src/on.js'
|
|
4
|
+
import { clearScheduleHandlers, setCronConverter } from '../src/every.js'
|
|
5
|
+
import { stopAllCronJobs } from '../src/cron-scheduler.js'
|
|
6
|
+
|
|
7
|
+
describe('Workflow cron scheduling', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
clearEventHandlers()
|
|
10
|
+
clearScheduleHandlers()
|
|
11
|
+
stopAllCronJobs()
|
|
12
|
+
vi.useFakeTimers()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
stopAllCronJobs()
|
|
17
|
+
vi.useRealTimers()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('$.every with cron patterns', () => {
|
|
21
|
+
it('should execute cron schedule at correct time', async () => {
|
|
22
|
+
const handler = vi.fn()
|
|
23
|
+
|
|
24
|
+
// Set to Monday 8:55am
|
|
25
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
26
|
+
|
|
27
|
+
const workflow = Workflow(($) => {
|
|
28
|
+
// Every hour at minute 0 (cron: 0 * * * *)
|
|
29
|
+
$.every.hour(handler)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
await workflow.start()
|
|
33
|
+
|
|
34
|
+
// Advance 5 minutes to 9:00am (next hour boundary)
|
|
35
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000)
|
|
36
|
+
|
|
37
|
+
expect(handler).toHaveBeenCalledTimes(1)
|
|
38
|
+
|
|
39
|
+
// Advance another hour to 10:00am
|
|
40
|
+
await vi.advanceTimersByTimeAsync(60 * 60 * 1000)
|
|
41
|
+
expect(handler).toHaveBeenCalledTimes(2)
|
|
42
|
+
|
|
43
|
+
await workflow.stop()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should execute day-specific schedule ($.every.Monday)', async () => {
|
|
47
|
+
const handler = vi.fn()
|
|
48
|
+
|
|
49
|
+
// Set to Sunday 11pm
|
|
50
|
+
vi.setSystemTime(new Date('2024-01-14T23:00:00'))
|
|
51
|
+
|
|
52
|
+
const workflow = Workflow(($) => {
|
|
53
|
+
// Every Monday at midnight (cron: 0 0 * * 1)
|
|
54
|
+
$.every.Monday(handler)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
await workflow.start()
|
|
58
|
+
|
|
59
|
+
// Advance 1 hour to Monday midnight
|
|
60
|
+
await vi.advanceTimersByTimeAsync(60 * 60 * 1000)
|
|
61
|
+
|
|
62
|
+
expect(handler).toHaveBeenCalledTimes(1)
|
|
63
|
+
|
|
64
|
+
await workflow.stop()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should execute day+time schedule ($.every.Monday.at9am)', async () => {
|
|
68
|
+
const handler = vi.fn()
|
|
69
|
+
|
|
70
|
+
// Set to Monday 8:30am
|
|
71
|
+
vi.setSystemTime(new Date('2024-01-15T08:30:00'))
|
|
72
|
+
|
|
73
|
+
const workflow = Workflow(($) => {
|
|
74
|
+
// Every Monday at 9am (cron: 0 9 * * 1)
|
|
75
|
+
$.every.Monday.at9am(handler)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
await workflow.start()
|
|
79
|
+
|
|
80
|
+
// Advance 30 minutes to 9:00am
|
|
81
|
+
await vi.advanceTimersByTimeAsync(30 * 60 * 1000)
|
|
82
|
+
|
|
83
|
+
expect(handler).toHaveBeenCalledTimes(1)
|
|
84
|
+
|
|
85
|
+
await workflow.stop()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should handle weekday schedule', async () => {
|
|
89
|
+
const handler = vi.fn()
|
|
90
|
+
|
|
91
|
+
// Set to Monday 8:55am
|
|
92
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
93
|
+
|
|
94
|
+
const workflow = Workflow(($) => {
|
|
95
|
+
$.every.weekday.at9am(handler)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
await workflow.start()
|
|
99
|
+
|
|
100
|
+
// Advance 5 minutes to 9am
|
|
101
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000)
|
|
102
|
+
|
|
103
|
+
expect(handler).toHaveBeenCalledTimes(1)
|
|
104
|
+
|
|
105
|
+
await workflow.stop()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should stop cron jobs on workflow stop', async () => {
|
|
109
|
+
const handler = vi.fn()
|
|
110
|
+
|
|
111
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
112
|
+
|
|
113
|
+
const workflow = Workflow(($) => {
|
|
114
|
+
$.every.hour(handler)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
await workflow.start()
|
|
118
|
+
|
|
119
|
+
// Advance to next hour
|
|
120
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000)
|
|
121
|
+
expect(handler).toHaveBeenCalledTimes(1)
|
|
122
|
+
|
|
123
|
+
// Stop workflow
|
|
124
|
+
await workflow.stop()
|
|
125
|
+
|
|
126
|
+
// Advance another hour - should NOT call handler
|
|
127
|
+
await vi.advanceTimersByTimeAsync(60 * 60 * 1000)
|
|
128
|
+
expect(handler).toHaveBeenCalledTimes(1) // Still 1
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('should handle multiple cron schedules', async () => {
|
|
132
|
+
const hourlyHandler = vi.fn()
|
|
133
|
+
const dailyHandler = vi.fn()
|
|
134
|
+
|
|
135
|
+
// Set to 8:55am
|
|
136
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
137
|
+
|
|
138
|
+
const workflow = Workflow(($) => {
|
|
139
|
+
$.every.hour(hourlyHandler)
|
|
140
|
+
$.every.day(dailyHandler)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
await workflow.start()
|
|
144
|
+
|
|
145
|
+
// Advance to 9:00am (hourly triggers)
|
|
146
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000)
|
|
147
|
+
expect(hourlyHandler).toHaveBeenCalledTimes(1)
|
|
148
|
+
expect(dailyHandler).toHaveBeenCalledTimes(0)
|
|
149
|
+
|
|
150
|
+
await workflow.stop()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should track schedule execution in history', async () => {
|
|
154
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
155
|
+
|
|
156
|
+
const workflow = Workflow(($) => {
|
|
157
|
+
$.every.hour(async ($) => {
|
|
158
|
+
$.log('Hourly task')
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
await workflow.start()
|
|
163
|
+
|
|
164
|
+
// Advance to 9:00am
|
|
165
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000)
|
|
166
|
+
|
|
167
|
+
// Check history has schedule entry
|
|
168
|
+
const history = workflow.state.history
|
|
169
|
+
const scheduleEntries = history.filter((h) => h.type === 'schedule')
|
|
170
|
+
expect(scheduleEntries.length).toBeGreaterThanOrEqual(1)
|
|
171
|
+
|
|
172
|
+
await workflow.stop()
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('$.every with natural language', () => {
|
|
177
|
+
it('should convert natural language to cron and execute', async () => {
|
|
178
|
+
const handler = vi.fn()
|
|
179
|
+
|
|
180
|
+
// Set up AI converter
|
|
181
|
+
setCronConverter(async (desc) => {
|
|
182
|
+
if (desc === 'every weekday morning') {
|
|
183
|
+
return '0 9 * * 1-5'
|
|
184
|
+
}
|
|
185
|
+
throw new Error(`Unknown: ${desc}`)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// Set to Monday 8:55am
|
|
189
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
190
|
+
|
|
191
|
+
const workflow = Workflow(($) => {
|
|
192
|
+
$.every('every weekday morning', handler)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
await workflow.start()
|
|
196
|
+
|
|
197
|
+
// Wait for async natural language parsing
|
|
198
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
199
|
+
|
|
200
|
+
// Advance to 9:00am
|
|
201
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000)
|
|
202
|
+
|
|
203
|
+
expect(handler).toHaveBeenCalledTimes(1)
|
|
204
|
+
|
|
205
|
+
await workflow.stop()
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('mixed schedules (interval + cron)', () => {
|
|
210
|
+
it('should support both interval and cron schedules', async () => {
|
|
211
|
+
const intervalHandler = vi.fn()
|
|
212
|
+
const cronHandler = vi.fn()
|
|
213
|
+
|
|
214
|
+
vi.setSystemTime(new Date('2024-01-15T08:55:00'))
|
|
215
|
+
|
|
216
|
+
const workflow = Workflow(($) => {
|
|
217
|
+
// Interval-based: every 30 seconds
|
|
218
|
+
$.every.seconds(30)(intervalHandler)
|
|
219
|
+
// Cron-based: every hour
|
|
220
|
+
$.every.hour(cronHandler)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
await workflow.start()
|
|
224
|
+
|
|
225
|
+
// Advance 30 seconds - interval triggers
|
|
226
|
+
await vi.advanceTimersByTimeAsync(30 * 1000)
|
|
227
|
+
expect(intervalHandler).toHaveBeenCalledTimes(1)
|
|
228
|
+
expect(cronHandler).toHaveBeenCalledTimes(0)
|
|
229
|
+
|
|
230
|
+
// Advance to next hour
|
|
231
|
+
await vi.advanceTimersByTimeAsync(5 * 60 * 1000 - 30 * 1000)
|
|
232
|
+
expect(cronHandler).toHaveBeenCalledTimes(1)
|
|
233
|
+
|
|
234
|
+
await workflow.stop()
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
describe('timer count', () => {
|
|
239
|
+
it('should include cron jobs in timer count', async () => {
|
|
240
|
+
const workflow = Workflow(($) => {
|
|
241
|
+
$.every.seconds(10)(() => {})
|
|
242
|
+
$.every.hour(() => {})
|
|
243
|
+
$.every.Monday.at9am(() => {})
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
await workflow.start()
|
|
247
|
+
|
|
248
|
+
// Should have 3 schedules (1 interval + 2 cron)
|
|
249
|
+
expect(workflow.timerCount).toBe(3)
|
|
250
|
+
|
|
251
|
+
await workflow.stop()
|
|
252
|
+
|
|
253
|
+
expect(workflow.timerCount).toBe(0)
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
})
|