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
@@ -16,7 +16,7 @@ describe('Workflow - unified $ API', () => {
16
16
 
17
17
  describe('Workflow()', () => {
18
18
  it('should create a workflow with $ context', () => {
19
- const workflow = Workflow($ => {
19
+ const workflow = Workflow(($) => {
20
20
  // Just accessing $ to verify it works
21
21
  expect($).toBeDefined()
22
22
  expect($.on).toBeDefined()
@@ -36,7 +36,7 @@ describe('Workflow - unified $ API', () => {
36
36
  })
37
37
 
38
38
  it('should capture event handlers registered via $.on', () => {
39
- const workflow = Workflow($ => {
39
+ const workflow = Workflow(($) => {
40
40
  $.on.Customer.created(() => {})
41
41
  $.on.Order.completed(() => {})
42
42
  })
@@ -49,7 +49,7 @@ describe('Workflow - unified $ API', () => {
49
49
  })
50
50
 
51
51
  it('should capture schedule handlers registered via $.every', () => {
52
- const workflow = Workflow($ => {
52
+ const workflow = Workflow(($) => {
53
53
  $.every.hour(() => {})
54
54
  $.every.Monday.at9am(() => {})
55
55
  })
@@ -58,7 +58,7 @@ describe('Workflow - unified $ API', () => {
58
58
  })
59
59
 
60
60
  it('should capture function source code', () => {
61
- const workflow = Workflow($ => {
61
+ const workflow = Workflow(($) => {
62
62
  $.on.Test.event(async (data, ctx) => {
63
63
  ctx.log('Test event', data)
64
64
  })
@@ -73,7 +73,7 @@ describe('Workflow - unified $ API', () => {
73
73
  it('should deliver events to registered handlers', async () => {
74
74
  const handler = vi.fn()
75
75
 
76
- const workflow = Workflow($ => {
76
+ const workflow = Workflow(($) => {
77
77
  $.on.Customer.created(handler)
78
78
  })
79
79
 
@@ -82,7 +82,7 @@ describe('Workflow - unified $ API', () => {
82
82
 
83
83
  expect(handler).toHaveBeenCalledTimes(1)
84
84
  expect(handler).toHaveBeenCalledWith(
85
- { id: '123', name: 'John' },
85
+ expect.objectContaining({ id: '123', name: 'John' }),
86
86
  expect.objectContaining({
87
87
  send: expect.any(Function),
88
88
  log: expect.any(Function),
@@ -93,7 +93,7 @@ describe('Workflow - unified $ API', () => {
93
93
  it('should allow chained event sending from handlers', async () => {
94
94
  const welcomeHandler = vi.fn()
95
95
 
96
- const workflow = Workflow($ => {
96
+ const workflow = Workflow(($) => {
97
97
  $.on.Customer.created(async (customer, $) => {
98
98
  await $.send('Email.welcome', { to: customer.email })
99
99
  })
@@ -105,13 +105,13 @@ describe('Workflow - unified $ API', () => {
105
105
  await workflow.send('Customer.created', { name: 'John', email: 'john@example.com' })
106
106
 
107
107
  expect(welcomeHandler).toHaveBeenCalledWith(
108
- { to: 'john@example.com' },
108
+ expect.objectContaining({ to: 'john@example.com' }),
109
109
  expect.anything()
110
110
  )
111
111
  })
112
112
 
113
113
  it('should track events in state history', async () => {
114
- const workflow = Workflow($ => {
114
+ const workflow = Workflow(($) => {
115
115
  $.on.Test.event(() => {})
116
116
  })
117
117
 
@@ -129,7 +129,7 @@ describe('Workflow - unified $ API', () => {
129
129
  it('should trigger schedule handlers', async () => {
130
130
  const handler = vi.fn()
131
131
 
132
- const workflow = Workflow($ => {
132
+ const workflow = Workflow(($) => {
133
133
  $.every.seconds(1)(handler)
134
134
  })
135
135
 
@@ -147,7 +147,7 @@ describe('Workflow - unified $ API', () => {
147
147
  it('should stop schedule handlers on stop', async () => {
148
148
  const handler = vi.fn()
149
149
 
150
- const workflow = Workflow($ => {
150
+ const workflow = Workflow(($) => {
151
151
  $.every.seconds(1)(handler)
152
152
  })
153
153
 
@@ -161,7 +161,7 @@ describe('Workflow - unified $ API', () => {
161
161
  })
162
162
 
163
163
  it('should support $.set and $.get for context data', async () => {
164
- const workflow = Workflow($ => {
164
+ const workflow = Workflow(($) => {
165
165
  $.on.Test.set(async (data, $) => {
166
166
  $.set('value', data.value)
167
167
  })
@@ -180,10 +180,7 @@ describe('Workflow - unified $ API', () => {
180
180
  })
181
181
 
182
182
  it('should use initial context from options', () => {
183
- const workflow = Workflow(
184
- $ => {},
185
- { context: { counter: 100 } }
186
- )
183
+ const workflow = Workflow(($) => {}, { context: { counter: 100 } })
187
184
 
188
185
  expect(workflow.state.context.counter).toBe(100)
189
186
  })
@@ -225,8 +222,14 @@ describe('Workflow - unified $ API', () => {
225
222
  await $.send('Test.event2', { b: 2 })
226
223
 
227
224
  expect($.emittedEvents).toHaveLength(2)
228
- expect($.emittedEvents[0]).toEqual({ event: 'Test.event1', data: { a: 1 } })
229
- expect($.emittedEvents[1]).toEqual({ event: 'Test.event2', data: { b: 2 } })
225
+ expect($.emittedEvents[0]).toEqual({
226
+ event: 'Test.event1',
227
+ data: expect.objectContaining({ a: 1 }),
228
+ })
229
+ expect($.emittedEvents[1]).toEqual({
230
+ event: 'Test.event2',
231
+ data: expect.objectContaining({ b: 2 }),
232
+ })
230
233
  })
231
234
 
232
235
  it('should support get/set', () => {
@@ -239,7 +242,7 @@ describe('Workflow - unified $ API', () => {
239
242
 
240
243
  describe('$.every patterns', () => {
241
244
  it('should support $.every.hour', () => {
242
- const workflow = Workflow($ => {
245
+ const workflow = Workflow(($) => {
243
246
  $.every.hour(() => {})
244
247
  })
245
248
 
@@ -251,7 +254,7 @@ describe('Workflow - unified $ API', () => {
251
254
  })
252
255
 
253
256
  it('should support $.every.Monday.at9am', () => {
254
- const workflow = Workflow($ => {
257
+ const workflow = Workflow(($) => {
255
258
  $.every.Monday.at9am(() => {})
256
259
  })
257
260
 
@@ -263,7 +266,7 @@ describe('Workflow - unified $ API', () => {
263
266
  })
264
267
 
265
268
  it('should support $.every.minutes(30)', () => {
266
- const workflow = Workflow($ => {
269
+ const workflow = Workflow(($) => {
267
270
  $.every.minutes(30)(() => {})
268
271
  })
269
272
 
@@ -275,7 +278,7 @@ describe('Workflow - unified $ API', () => {
275
278
  })
276
279
 
277
280
  it('should support $.every("natural language")', () => {
278
- const workflow = Workflow($ => {
281
+ const workflow = Workflow(($) => {
279
282
  $.every('first Monday of the month', () => {})
280
283
  })
281
284
 
package/tsconfig.json CHANGED
@@ -2,7 +2,9 @@
2
2
  "extends": "../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "rootDir": "src",
5
- "outDir": "dist"
5
+ "outDir": "dist",
6
+ "lib": ["ES2022", "ESNext.Disposable"],
7
+ "types": ["@cloudflare/workers-types", "node"]
6
8
  },
7
9
  "include": ["src/**/*"],
8
10
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
package/vitest.config.ts CHANGED
@@ -2,7 +2,44 @@ import { defineConfig } from 'vitest/config'
2
2
 
3
3
  export default defineConfig({
4
4
  test: {
5
+ // CRITICAL: Limit concurrency to prevent resource exhaustion
6
+ maxConcurrency: 1,
7
+ maxWorkers: 1,
8
+ minWorkers: 1,
9
+ fileParallelism: false,
10
+
5
11
  globals: true,
6
- include: ['test/**/*.test.ts'],
12
+ environment: 'node',
13
+ include: ['test/**/*.test.ts', 'src/**/*.test.ts'],
14
+ testTimeout: 30000,
15
+ hookTimeout: 15000,
16
+
17
+ // Run tests sequentially with better memory isolation
18
+ pool: 'forks',
19
+ poolOptions: {
20
+ forks: {
21
+ singleFork: true,
22
+ },
23
+ },
24
+
25
+ // Coverage configuration
26
+ coverage: {
27
+ provider: 'v8',
28
+ reporter: ['text', 'json', 'html'],
29
+ include: ['src/**/*.ts'],
30
+ exclude: ['**/*.test.ts', '**/__tests__/**', '**/node_modules/**'],
31
+ thresholds: {
32
+ statements: 65,
33
+ branches: 60,
34
+ functions: 60,
35
+ lines: 65,
36
+ },
37
+ },
38
+
39
+ // Alias for cloudflare:workers mock (shared across monorepo)
40
+ alias: {
41
+ 'cloudflare:workers': new URL('../config/mocks/cloudflare-workers.ts', import.meta.url)
42
+ .pathname,
43
+ },
7
44
  },
8
45
  })
@@ -0,0 +1,44 @@
1
+ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'
2
+
3
+ export default defineWorkersConfig({
4
+ test: {
5
+ // CRITICAL: Limit concurrency to prevent resource exhaustion
6
+ maxConcurrency: 1,
7
+ maxWorkers: 1,
8
+ minWorkers: 1,
9
+ fileParallelism: false,
10
+
11
+ poolOptions: {
12
+ workers: {
13
+ wrangler: { configPath: './wrangler.jsonc' },
14
+ // Disable isolated storage for Workflows tests
15
+ // Workflows require non-isolated mode due to how miniflare handles them
16
+ isolatedStorage: false,
17
+ singleWorker: true,
18
+ miniflare: {
19
+ compatibilityDate: '2025-01-20',
20
+ compatibilityFlags: ['nodejs_compat_v2'],
21
+ },
22
+ },
23
+ },
24
+
25
+ // Only include worker-specific tests
26
+ include: ['test/worker/**/*.test.ts'],
27
+ testTimeout: 60000,
28
+ hookTimeout: 30000,
29
+
30
+ // Coverage configuration
31
+ coverage: {
32
+ provider: 'v8',
33
+ reporter: ['text', 'json', 'html'],
34
+ include: ['src/**/*.ts'],
35
+ exclude: ['**/*.test.ts', '**/__tests__/**', '**/node_modules/**'],
36
+ thresholds: {
37
+ statements: 65,
38
+ branches: 60,
39
+ functions: 60,
40
+ lines: 65,
41
+ },
42
+ },
43
+ },
44
+ })
package/wrangler.jsonc ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ // Wrangler configuration for ai-workflows worker tests
3
+ // Used by @cloudflare/vitest-pool-workers for testing
4
+ //
5
+ // Configures Cloudflare Workflows binding for testing DurableStep
6
+ // with real Workflows runtime semantics.
7
+
8
+ "name": "ai-workflows",
9
+ "main": "src/worker.ts",
10
+ "compatibility_date": "2025-01-20",
11
+ "compatibility_flags": ["nodejs_compat_v2"],
12
+
13
+ // Cloudflare Workflows binding
14
+ // This binds the TestWorkflow class to WORKFLOW for testing DurableStep
15
+ "workflows": [
16
+ {
17
+ "name": "test-workflow",
18
+ "binding": "WORKFLOW",
19
+ "class_name": "TestWorkflow"
20
+ }
21
+ ]
22
+ }
@@ -1,7 +0,0 @@
1
-
2
- > ai-workflows@2.0.3 test /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-workflows
3
- > vitest "--run"
4
-
5
-
6
- RUN v2.1.9 /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-workflows
7
-
package/src/context.js DELETED
@@ -1,83 +0,0 @@
1
- /**
2
- * Workflow context implementation
3
- */
4
- /**
5
- * Create a workflow context
6
- */
7
- export function createWorkflowContext(eventBus) {
8
- const workflowState = {
9
- context: {},
10
- history: [],
11
- };
12
- const addHistory = (entry) => {
13
- workflowState.history.push({
14
- ...entry,
15
- timestamp: Date.now(),
16
- });
17
- };
18
- // Create no-op proxies for on/every (these are used in send context, not workflow setup)
19
- const noOpOnProxy = new Proxy({}, {
20
- get() {
21
- return new Proxy({}, {
22
- get() {
23
- return () => { };
24
- }
25
- });
26
- }
27
- });
28
- const noOpEveryProxy = new Proxy(function () { }, {
29
- get() {
30
- return () => () => { };
31
- },
32
- apply() { }
33
- });
34
- return {
35
- async send(event, data) {
36
- addHistory({ type: 'event', name: event, data });
37
- await eventBus.emit(event, data);
38
- },
39
- async do(_event, _data) {
40
- throw new Error('$.do not available in this context');
41
- },
42
- async try(_event, _data) {
43
- throw new Error('$.try not available in this context');
44
- },
45
- on: noOpOnProxy,
46
- every: noOpEveryProxy,
47
- state: workflowState.context,
48
- getState() {
49
- // Return a deep copy to prevent mutation
50
- return {
51
- current: workflowState.current,
52
- context: { ...workflowState.context },
53
- history: [...workflowState.history],
54
- };
55
- },
56
- set(key, value) {
57
- workflowState.context[key] = value;
58
- },
59
- get(key) {
60
- return workflowState.context[key];
61
- },
62
- log(message, data) {
63
- addHistory({ type: 'action', name: 'log', data: { message, data } });
64
- console.log(`[workflow] ${message}`, data ?? '');
65
- },
66
- };
67
- }
68
- /**
69
- * Create an isolated workflow context (not connected to event bus)
70
- * Useful for testing or standalone execution
71
- */
72
- export function createIsolatedContext() {
73
- const emittedEvents = [];
74
- const ctx = createWorkflowContext({
75
- async emit(event, data) {
76
- emittedEvents.push({ event, data });
77
- },
78
- });
79
- return {
80
- ...ctx,
81
- getEmittedEvents: () => [...emittedEvents],
82
- };
83
- }
package/src/every.js DELETED
@@ -1,267 +0,0 @@
1
- /**
2
- * Schedule registration using natural language
3
- *
4
- * Usage:
5
- * every.hour($ => { ... })
6
- * every.Thursday.at8am($ => { ... })
7
- * every.weekday.at9am($ => { ... })
8
- * every('hour during business hours', $ => { ... })
9
- * every('first Monday of the month at 9am', $ => { ... })
10
- */
11
- /**
12
- * Registry of schedule handlers
13
- */
14
- const scheduleRegistry = [];
15
- /**
16
- * Get all registered schedule handlers
17
- */
18
- export function getScheduleHandlers() {
19
- return [...scheduleRegistry];
20
- }
21
- /**
22
- * Clear all registered schedule handlers
23
- */
24
- export function clearScheduleHandlers() {
25
- scheduleRegistry.length = 0;
26
- }
27
- /**
28
- * Register a schedule handler directly
29
- */
30
- export function registerScheduleHandler(interval, handler) {
31
- scheduleRegistry.push({
32
- interval,
33
- handler,
34
- source: handler.toString(),
35
- });
36
- }
37
- /**
38
- * Well-known cron patterns for common schedules
39
- */
40
- const KNOWN_PATTERNS = {
41
- // Time units
42
- 'second': '* * * * * *',
43
- 'minute': '* * * * *',
44
- 'hour': '0 * * * *',
45
- 'day': '0 0 * * *',
46
- 'week': '0 0 * * 0',
47
- 'month': '0 0 1 * *',
48
- 'year': '0 0 1 1 *',
49
- // Days of week
50
- 'Monday': '0 0 * * 1',
51
- 'Tuesday': '0 0 * * 2',
52
- 'Wednesday': '0 0 * * 3',
53
- 'Thursday': '0 0 * * 4',
54
- 'Friday': '0 0 * * 5',
55
- 'Saturday': '0 0 * * 6',
56
- 'Sunday': '0 0 * * 0',
57
- // Common patterns
58
- 'weekday': '0 0 * * 1-5',
59
- 'weekend': '0 0 * * 0,6',
60
- 'midnight': '0 0 * * *',
61
- 'noon': '0 12 * * *',
62
- };
63
- /**
64
- * Time suffixes for day-based schedules
65
- */
66
- const TIME_PATTERNS = {
67
- 'at6am': { hour: 6, minute: 0 },
68
- 'at7am': { hour: 7, minute: 0 },
69
- 'at8am': { hour: 8, minute: 0 },
70
- 'at9am': { hour: 9, minute: 0 },
71
- 'at10am': { hour: 10, minute: 0 },
72
- 'at11am': { hour: 11, minute: 0 },
73
- 'at12pm': { hour: 12, minute: 0 },
74
- 'atnoon': { hour: 12, minute: 0 },
75
- 'at1pm': { hour: 13, minute: 0 },
76
- 'at2pm': { hour: 14, minute: 0 },
77
- 'at3pm': { hour: 15, minute: 0 },
78
- 'at4pm': { hour: 16, minute: 0 },
79
- 'at5pm': { hour: 17, minute: 0 },
80
- 'at6pm': { hour: 18, minute: 0 },
81
- 'at7pm': { hour: 19, minute: 0 },
82
- 'at8pm': { hour: 20, minute: 0 },
83
- 'at9pm': { hour: 21, minute: 0 },
84
- 'atmidnight': { hour: 0, minute: 0 },
85
- };
86
- /**
87
- * Parse a known pattern or return null
88
- */
89
- function parseKnownPattern(pattern) {
90
- return KNOWN_PATTERNS[pattern] ?? null;
91
- }
92
- /**
93
- * Combine a day pattern with a time pattern
94
- */
95
- function combineWithTime(baseCron, time) {
96
- const parts = baseCron.split(' ');
97
- parts[0] = String(time.minute);
98
- parts[1] = String(time.hour);
99
- return parts.join(' ');
100
- }
101
- /**
102
- * AI-powered cron conversion (placeholder - will use ai-functions)
103
- */
104
- let cronConverter = null;
105
- /**
106
- * Set the AI cron converter function
107
- */
108
- export function setCronConverter(converter) {
109
- cronConverter = converter;
110
- }
111
- /**
112
- * Convert natural language to cron expression
113
- */
114
- export async function toCron(description) {
115
- // First check known patterns
116
- const known = parseKnownPattern(description);
117
- if (known)
118
- return known;
119
- // If we have an AI converter, use it
120
- if (cronConverter) {
121
- return cronConverter(description);
122
- }
123
- // Otherwise, assume it's already a cron expression or throw
124
- if (/^[\d\*\-\/\,\s]+$/.test(description)) {
125
- return description;
126
- }
127
- throw new Error(`Unknown schedule pattern: "${description}". ` +
128
- `Set up AI conversion with setCronConverter() for natural language schedules.`);
129
- }
130
- /**
131
- * Create the `every` proxy
132
- */
133
- function createEveryProxy() {
134
- const handler = {
135
- get(_target, prop) {
136
- // Check if it's a known pattern
137
- const pattern = KNOWN_PATTERNS[prop];
138
- if (pattern) {
139
- // Return an object that can either be called directly or have time accessors
140
- const result = (handlerFn) => {
141
- registerScheduleHandler({ type: 'cron', expression: pattern, natural: prop }, handlerFn);
142
- };
143
- // Add time accessors
144
- return new Proxy(result, {
145
- get(_t, timeKey) {
146
- const time = TIME_PATTERNS[timeKey];
147
- if (time) {
148
- const cron = combineWithTime(pattern, time);
149
- return (handlerFn) => {
150
- registerScheduleHandler({ type: 'cron', expression: cron, natural: `${prop}.${timeKey}` }, handlerFn);
151
- };
152
- }
153
- return undefined;
154
- },
155
- apply(_t, _thisArg, args) {
156
- registerScheduleHandler({ type: 'cron', expression: pattern, natural: prop }, args[0]);
157
- }
158
- });
159
- }
160
- // Check for plural time units (e.g., seconds(5), minutes(30))
161
- const pluralUnits = {
162
- seconds: 'second',
163
- minutes: 'minute',
164
- hours: 'hour',
165
- days: 'day',
166
- weeks: 'week',
167
- };
168
- if (pluralUnits[prop]) {
169
- return (value) => (handlerFn) => {
170
- registerScheduleHandler({ type: pluralUnits[prop], value, natural: `${value} ${prop}` }, handlerFn);
171
- };
172
- }
173
- return undefined;
174
- },
175
- apply(_target, _thisArg, args) {
176
- // Called as every('natural language description', handler)
177
- const [description, handler] = args;
178
- if (typeof description === 'string' && typeof handler === 'function') {
179
- // Register with natural language - will be converted to cron at runtime
180
- registerScheduleHandler({ type: 'natural', description }, handler);
181
- }
182
- }
183
- };
184
- return new Proxy(function () { }, handler);
185
- }
186
- /**
187
- * The `every` function/object for registering scheduled handlers
188
- *
189
- * @example
190
- * ```ts
191
- * import { every } from 'ai-workflows'
192
- *
193
- * // Simple intervals
194
- * every.hour($ => $.log('Hourly task'))
195
- * every.day($ => $.log('Daily task'))
196
- *
197
- * // Day + time combinations
198
- * every.Monday.at9am($ => $.log('Monday morning standup'))
199
- * every.weekday.at8am($ => $.log('Workday start'))
200
- * every.Friday.at5pm($ => $.log('End of week report'))
201
- *
202
- * // Plural intervals with values
203
- * every.minutes(30)($ => $.log('Every 30 minutes'))
204
- * every.hours(4)($ => $.log('Every 4 hours'))
205
- *
206
- * // Natural language (requires AI converter)
207
- * every('hour during business hours', $ => { ... })
208
- * every('first Monday of the month at 9am', $ => { ... })
209
- * every('every 15 minutes between 9am and 5pm on weekdays', $ => { ... })
210
- * ```
211
- */
212
- export const every = createEveryProxy();
213
- /**
214
- * Convert interval to milliseconds (for simulation/testing)
215
- */
216
- export function intervalToMs(interval) {
217
- switch (interval.type) {
218
- case 'second':
219
- return (interval.value ?? 1) * 1000;
220
- case 'minute':
221
- return (interval.value ?? 1) * 60 * 1000;
222
- case 'hour':
223
- return (interval.value ?? 1) * 60 * 60 * 1000;
224
- case 'day':
225
- return (interval.value ?? 1) * 24 * 60 * 60 * 1000;
226
- case 'week':
227
- return (interval.value ?? 1) * 7 * 24 * 60 * 60 * 1000;
228
- case 'cron':
229
- case 'natural':
230
- // Cron/natural expressions need special handling
231
- return 0;
232
- }
233
- }
234
- /**
235
- * Format interval for display
236
- */
237
- export function formatInterval(interval) {
238
- if ('natural' in interval && interval.natural) {
239
- return interval.natural;
240
- }
241
- switch (interval.type) {
242
- case 'second':
243
- return interval.value && interval.value > 1
244
- ? `every ${interval.value} seconds`
245
- : 'every second';
246
- case 'minute':
247
- return interval.value && interval.value > 1
248
- ? `every ${interval.value} minutes`
249
- : 'every minute';
250
- case 'hour':
251
- return interval.value && interval.value > 1
252
- ? `every ${interval.value} hours`
253
- : 'every hour';
254
- case 'day':
255
- return interval.value && interval.value > 1
256
- ? `every ${interval.value} days`
257
- : 'every day';
258
- case 'week':
259
- return interval.value && interval.value > 1
260
- ? `every ${interval.value} weeks`
261
- : 'every week';
262
- case 'cron':
263
- return `cron: ${interval.expression}`;
264
- case 'natural':
265
- return interval.description;
266
- }
267
- }