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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -1
- package/README.md +305 -184
- package/dist/barrier.d.ts +159 -0
- package/dist/barrier.d.ts.map +1 -0
- package/dist/barrier.js +377 -0
- package/dist/barrier.js.map +1 -0
- package/dist/cascade-context.d.ts +149 -0
- package/dist/cascade-context.d.ts.map +1 -0
- package/dist/cascade-context.js +324 -0
- package/dist/cascade-context.js.map +1 -0
- package/dist/cascade-executor.d.ts +196 -0
- package/dist/cascade-executor.d.ts.map +1 -0
- package/dist/cascade-executor.js +384 -0
- package/dist/cascade-executor.js.map +1 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +27 -8
- 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/dependency-graph.d.ts +157 -0
- package/dist/dependency-graph.d.ts.map +1 -0
- package/dist/dependency-graph.js +382 -0
- package/dist/dependency-graph.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/every.d.ts +31 -2
- package/dist/every.d.ts.map +1 -1
- package/dist/every.js +63 -32
- package/dist/every.js.map +1 -1
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +8 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/topological-sort.d.ts +121 -0
- package/dist/graph/topological-sort.d.ts.map +1 -0
- package/dist/graph/topological-sort.js +292 -0
- package/dist/graph/topological-sort.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -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 +35 -10
- package/dist/on.d.ts.map +1 -1
- package/dist/on.js +53 -19
- 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 +77 -0
- package/dist/timer-registry.d.ts.map +1 -0
- package/dist/timer-registry.js +154 -0
- package/dist/timer-registry.js.map +1 -0
- package/dist/types.d.ts +105 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -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 +136 -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 +21 -4
- package/src/__tests__/digital-objects-adapter.test.ts +274 -0
- package/src/__tests__/durable-workflow.test.ts +297 -0
- package/src/barrier.ts +507 -0
- package/src/cascade-context.ts +495 -0
- package/src/cascade-executor.ts +588 -0
- package/src/context.ts +51 -17
- package/src/cron-parser.ts +347 -0
- package/src/cron-scheduler.ts +239 -0
- package/src/database-context.ts +658 -0
- package/src/dependency-graph.ts +518 -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/every.ts +104 -35
- package/src/graph/index.ts +19 -0
- package/src/graph/topological-sort.ts +412 -0
- package/src/index.ts +147 -0
- package/src/logger.ts +148 -0
- package/src/on.ts +81 -26
- package/src/runtime.ts +436 -0
- package/src/send.ts +4 -5
- package/src/telemetry.ts +577 -0
- package/src/timer-registry.ts +179 -0
- package/src/types.ts +146 -10
- 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 +199 -355
- package/test/barrier-join.test.ts +442 -0
- package/test/barrier-unhandled-rejections.test.ts +359 -0
- package/test/cascade-context.test.ts +390 -0
- package/test/cascade-executor.test.ts +852 -0
- 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/dependency-graph.test.ts +512 -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/graph/topological-sort.test.ts +586 -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 +353 -0
- package/test/send-race-conditions.test.ts +400 -0
- package/test/type-safety-every.test.ts +303 -0
- 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 -7
- 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,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workflows adapter for {@link DurableExecutionAdapter}.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the backend-agnostic port defined in `./durable-execution.ts` to
|
|
5
|
+
* Cloudflare's class-based {@link https://developers.cloudflare.com/workflows/ Workflows runtime}.
|
|
6
|
+
* CF Workflows is the production default per
|
|
7
|
+
* {@link ../../docs/adr/0004-durable-execution-cf-workflows-default.md ADR-0004}:
|
|
8
|
+
* the recently expanded limits (25K steps default-configurable, 50K concurrent
|
|
9
|
+
* instances, 365-day max sleep, 1 GB persisted state) plus zero per-step
|
|
10
|
+
* billing make it the cost-optimal backend for cascade-heavy workloads.
|
|
11
|
+
*
|
|
12
|
+
* ## How the bridge works
|
|
13
|
+
*
|
|
14
|
+
* CF Workflows uses a class-based dispatch model — users author a
|
|
15
|
+
* {@link WorkflowEntrypointLike} subclass with `async run(event, step)`, and
|
|
16
|
+
* the runtime instantiates it for each invocation. Our port's
|
|
17
|
+
* `run(name, fn, input)` is callback-shaped. The adapter resolves the
|
|
18
|
+
* impedance mismatch with two complementary surfaces:
|
|
19
|
+
*
|
|
20
|
+
* 1. **Workflow-function registry.** Callers register named workflow bodies
|
|
21
|
+
* against the adapter via {@link CloudflareWorkflowsDurableExecution.register}
|
|
22
|
+
* (or implicitly the first time {@link CloudflareWorkflowsDurableExecution.run}
|
|
23
|
+
* is called with that name). The registry is a plain {@link Map}
|
|
24
|
+
* maintained on the adapter instance.
|
|
25
|
+
*
|
|
26
|
+
* 2. **`createWorkflowEntrypoint(adapter)`.** Returns a
|
|
27
|
+
* {@link WorkflowEntrypointLike} subclass whose `run(event, step)`
|
|
28
|
+
* reads the workflow name from `event.payload.__wfName`, looks the body
|
|
29
|
+
* up in the registry, and invokes it with a {@link WorkflowContext} that
|
|
30
|
+
* delegates each port primitive to the CF `step` argument. Users export
|
|
31
|
+
* this class from their worker module and wire it through `wrangler.jsonc`:
|
|
32
|
+
*
|
|
33
|
+
* ```jsonc
|
|
34
|
+
* "workflows": [{
|
|
35
|
+
* "name": "cascade",
|
|
36
|
+
* "binding": "WORKFLOW",
|
|
37
|
+
* "class_name": "MyWorkflowEntrypoint"
|
|
38
|
+
* }]
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* The adapter's `run()` triggers a workflow by calling `binding.create({
|
|
42
|
+
* params: { __wfName, __wfInput } })` against the supplied
|
|
43
|
+
* {@link WorkflowsBindingLike}. By default it polls
|
|
44
|
+
* `instance.status()` until completion and returns the workflow output;
|
|
45
|
+
* callers that need fire-and-forget can opt out via
|
|
46
|
+
* {@link CloudflareWorkflowsDurableExecutionOptions.waitForCompletion}.
|
|
47
|
+
*
|
|
48
|
+
* ## Step / sleep / waitForEvent translation
|
|
49
|
+
*
|
|
50
|
+
* - `ctx.step(name, fn)` → `step.do(name, fn)`
|
|
51
|
+
* - `ctx.step(name, cfg, fn)` → `step.do(name, cfg, fn)`
|
|
52
|
+
* - `ctx.sleep(duration)` → `step.sleep(autoStepName, duration)`
|
|
53
|
+
* - `ctx.sleepUntil(date)` → `step.sleepUntil(autoStepName, date)`
|
|
54
|
+
* - `ctx.waitForEvent(name, t?)` → `step.waitForEvent(autoStepName, { type: name, timeout: t })`
|
|
55
|
+
*
|
|
56
|
+
* CF requires every primitive to receive a stable name; our port's `sleep` /
|
|
57
|
+
* `sleepUntil` / `waitForEvent` don't. The bridge synthesises a deterministic
|
|
58
|
+
* name from a per-context counter (`__sleep__1`, `__waitForEvent__Order.placed__1`)
|
|
59
|
+
* incremented in body order. Bodies that take the same control-flow path on
|
|
60
|
+
* replay therefore see identical names — the determinism rule applies.
|
|
61
|
+
*
|
|
62
|
+
* Callers who want full control over step naming for sleeps/waits should use
|
|
63
|
+
* {@link DurableExecutionAdapter.step} (or `ctx.step`) wrapping the desired
|
|
64
|
+
* primitive: e.g. `await ctx.step('payment-window', () => sleepUntil(deadline))`.
|
|
65
|
+
*
|
|
66
|
+
* ## Schedules
|
|
67
|
+
*
|
|
68
|
+
* CF Workflows do not support imperative cron registration through the binding;
|
|
69
|
+
* scheduled triggers are declared in `wrangler.jsonc` under `[triggers] crons`
|
|
70
|
+
* and routed to a Worker `scheduled()` handler. The adapter's
|
|
71
|
+
* {@link DurableExecutionAdapter.schedule} therefore registers the workflow
|
|
72
|
+
* body against the adapter and returns a {@link Subscription} whose `id` is
|
|
73
|
+
* the workflow name; the user's worker `scheduled()` handler must call
|
|
74
|
+
* {@link CloudflareWorkflowsDurableExecution.runSchedule} (or the adapter's
|
|
75
|
+
* `run()`) when the cron fires. {@link CloudflareWorkflowsDurableExecution.defineSchedule}
|
|
76
|
+
* is an alias for `schedule()` that emphasises the wrangler-coordinated nature
|
|
77
|
+
* of CF scheduling.
|
|
78
|
+
*
|
|
79
|
+
* ## Rules of Workflows (CF-imposed; universal)
|
|
80
|
+
*
|
|
81
|
+
* 1. **Steps must be idempotent.** CF re-executes step bodies on replay
|
|
82
|
+
* after a hibernation boundary or transient failure. Wrap external
|
|
83
|
+
* side-effects so a duplicate invocation is observably equivalent to a
|
|
84
|
+
* single one.
|
|
85
|
+
*
|
|
86
|
+
* 2. **Step names must be deterministic.** CF uses the step name as the
|
|
87
|
+
* memoization key. Random ids, timestamps, or run-specific values in
|
|
88
|
+
* step names break replay.
|
|
89
|
+
*
|
|
90
|
+
* 3. **State must flow through step returns.** Variables defined in the
|
|
91
|
+
* workflow body but outside a step DO NOT survive hibernation. Read
|
|
92
|
+
* inputs at the top of a step and return only what subsequent steps
|
|
93
|
+
* need.
|
|
94
|
+
*
|
|
95
|
+
* 4. **Workflow bodies must be deterministic.** Two replays of the same
|
|
96
|
+
* input must take the same control-flow path. Push non-determinism
|
|
97
|
+
* (clocks, randomness, network reads) into steps so CF can memoize the
|
|
98
|
+
* result.
|
|
99
|
+
*
|
|
100
|
+
* 5. **Use `step.sleep` / `step.sleepUntil` / `step.waitForEvent`.** Never
|
|
101
|
+
* `setTimeout` or polling — only the runtime knows how to suspend and
|
|
102
|
+
* resume the workflow.
|
|
103
|
+
*
|
|
104
|
+
* ## Limits
|
|
105
|
+
*
|
|
106
|
+
* Declared via {@link CloudflareWorkflowsDurableExecution.limits}:
|
|
107
|
+
*
|
|
108
|
+
* - **Steps per workflow:** 25,000 (default-configurable per Mar 2026
|
|
109
|
+
* change; see ADR-0004).
|
|
110
|
+
* - **Concurrent instances:** 50,000 per account (Apr 2026 change).
|
|
111
|
+
* - **Maximum sleep:** 365 days.
|
|
112
|
+
* - **Per-step / per-event payload:** 1 MiB (CF Workers RPC limit).
|
|
113
|
+
*
|
|
114
|
+
* @see {@link ../../docs/adr/0004-durable-execution-cf-workflows-default.md ADR-0004}
|
|
115
|
+
* @see {@link https://developers.cloudflare.com/workflows/build/workflows-api/ Workflows API}
|
|
116
|
+
* @see {@link https://developers.cloudflare.com/workflows/build/rules-of-workflows/ Rules of Workflows}
|
|
117
|
+
*
|
|
118
|
+
* @example Wiring an adapter
|
|
119
|
+
* ```ts
|
|
120
|
+
* // worker.ts
|
|
121
|
+
* import {
|
|
122
|
+
* createCloudflareWorkflowsDurableExecution,
|
|
123
|
+
* createWorkflowEntrypoint,
|
|
124
|
+
* } from 'ai-workflows/durable-execution'
|
|
125
|
+
*
|
|
126
|
+
* type Env = { WORKFLOW: import('cloudflare:workers').Workflow }
|
|
127
|
+
*
|
|
128
|
+
* const dx = createCloudflareWorkflowsDurableExecution({
|
|
129
|
+
* binding: () => env.WORKFLOW, // resolved per-request
|
|
130
|
+
* })
|
|
131
|
+
*
|
|
132
|
+
* dx.register('cascade', async (ctx) => {
|
|
133
|
+
* const plan = await ctx.step('plan', () => generatePlan(ctx.input))
|
|
134
|
+
* await ctx.sleep('1 minute')
|
|
135
|
+
* return ctx.step('write', () => writeAll(plan))
|
|
136
|
+
* })
|
|
137
|
+
*
|
|
138
|
+
* // The class wrangler binds; CF instantiates it on each run.
|
|
139
|
+
* export const MyWorkflow = createWorkflowEntrypoint(dx)
|
|
140
|
+
*
|
|
141
|
+
* export default {
|
|
142
|
+
* async fetch(req: Request, env: Env) {
|
|
143
|
+
* // Trigger from anywhere — adapter.run() goes through the binding.
|
|
144
|
+
* const result = await dx.run('cascade', undefined as never, { customerId: 'c-1' })
|
|
145
|
+
* return Response.json(result)
|
|
146
|
+
* },
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @packageDocumentation
|
|
151
|
+
*/
|
|
152
|
+
import { DurableStepError, WaitForEventTimeoutError, } from './durable-execution.js';
|
|
153
|
+
const CLOUDFLARE_WORKFLOWS_LIMITS = {
|
|
154
|
+
maxSteps: 25_000,
|
|
155
|
+
maxConcurrentInstances: 50_000,
|
|
156
|
+
maxSleepDays: 365,
|
|
157
|
+
maxPayloadBytes: 1_048_576,
|
|
158
|
+
};
|
|
159
|
+
function isEnvelope(payload) {
|
|
160
|
+
return (typeof payload === 'object' &&
|
|
161
|
+
payload !== null &&
|
|
162
|
+
'__wfName' in payload &&
|
|
163
|
+
typeof payload.__wfName === 'string' &&
|
|
164
|
+
'__wfInput' in payload);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Construct a Cloudflare Workflows {@link DurableExecutionAdapter}.
|
|
168
|
+
*
|
|
169
|
+
* The adapter satisfies the full port contract by translating each call into
|
|
170
|
+
* its CF equivalent (see module docs). Step bodies execute inside CF's
|
|
171
|
+
* runtime and inherit CF's idempotency, replay, and hibernation semantics.
|
|
172
|
+
*
|
|
173
|
+
* **The adapter does not run workflow bodies in-process.** Calling `run()`
|
|
174
|
+
* triggers a CF Workflow via the supplied binding; the body runs on CF's
|
|
175
|
+
* infrastructure and the adapter polls (or returns the instance handle).
|
|
176
|
+
* For tests that need to exercise the body directly without a CF runtime,
|
|
177
|
+
* use {@link createInProcessDurableExecution} or
|
|
178
|
+
* {@link createInMemoryDurableExecution} instead.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { createCloudflareWorkflowsDurableExecution, createWorkflowEntrypoint } from 'ai-workflows/durable-execution'
|
|
183
|
+
*
|
|
184
|
+
* type Env = { WORKFLOW: Workflow }
|
|
185
|
+
*
|
|
186
|
+
* const dx = createCloudflareWorkflowsDurableExecution({ binding: () => env.WORKFLOW })
|
|
187
|
+
* dx.register('hello', async (ctx) => `hi, ${ctx.input}`)
|
|
188
|
+
*
|
|
189
|
+
* export const HelloWorkflow = createWorkflowEntrypoint(dx)
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
export function createCloudflareWorkflowsDurableExecution(options) {
|
|
193
|
+
const waitForCompletion = options.waitForCompletion ?? true;
|
|
194
|
+
const pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
195
|
+
const pollTimeoutMs = options.pollTimeoutMs ?? 24 * 60 * 60 * 1000;
|
|
196
|
+
const delay = options.delay ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
197
|
+
const registry = new Map();
|
|
198
|
+
const schedules = new Map();
|
|
199
|
+
function resolveBinding() {
|
|
200
|
+
const b = options.binding;
|
|
201
|
+
return typeof b === 'function' ? b() : b;
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Workflow context bridge — invoked by the entrypoint handler with the CF
|
|
205
|
+
// `step` argument. Each port primitive translates to the matching CF call.
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
function buildContext(name, instanceId, input, step) {
|
|
208
|
+
// Auto-naming counter for sleep/waitForEvent (CF requires every primitive
|
|
209
|
+
// to receive a stable, deterministic name). The body's control flow is
|
|
210
|
+
// deterministic, so a strictly increasing counter yields stable names
|
|
211
|
+
// across replays.
|
|
212
|
+
let sleepCounter = 0;
|
|
213
|
+
let sleepUntilCounter = 0;
|
|
214
|
+
const waitCounters = new Map();
|
|
215
|
+
const ctx = {
|
|
216
|
+
input,
|
|
217
|
+
instanceId,
|
|
218
|
+
name,
|
|
219
|
+
step: ((stepName, configOrFn, maybeFn) => {
|
|
220
|
+
if (typeof configOrFn === 'function') {
|
|
221
|
+
return step.do(stepName, configOrFn);
|
|
222
|
+
}
|
|
223
|
+
return step.do(stepName, configOrFn, maybeFn);
|
|
224
|
+
}),
|
|
225
|
+
async sleep(duration) {
|
|
226
|
+
const stepName = `__sleep__${++sleepCounter}`;
|
|
227
|
+
await step.sleep(stepName, duration);
|
|
228
|
+
},
|
|
229
|
+
async sleepUntil(date) {
|
|
230
|
+
const stepName = `__sleepUntil__${++sleepUntilCounter}`;
|
|
231
|
+
await step.sleepUntil(stepName, date);
|
|
232
|
+
},
|
|
233
|
+
async waitForEvent(eventName, timeout) {
|
|
234
|
+
const seq = (waitCounters.get(eventName) ?? 0) + 1;
|
|
235
|
+
waitCounters.set(eventName, seq);
|
|
236
|
+
const stepName = `__waitForEvent__${eventName}__${seq}`;
|
|
237
|
+
try {
|
|
238
|
+
const opts = { type: eventName };
|
|
239
|
+
if (timeout !== undefined)
|
|
240
|
+
opts.timeout = timeout;
|
|
241
|
+
const result = await step.waitForEvent(stepName, opts);
|
|
242
|
+
// CF returns { payload, type, timestamp } — unwrap to the user-shaped
|
|
243
|
+
// value when the envelope is present; otherwise pass through.
|
|
244
|
+
if (result !== null &&
|
|
245
|
+
typeof result === 'object' &&
|
|
246
|
+
'payload' in result) {
|
|
247
|
+
return result.payload;
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
// CF surfaces wait timeouts as thrown errors; normalise to the
|
|
253
|
+
// port's WaitForEventTimeoutError when the message indicates a
|
|
254
|
+
// timeout. Otherwise rethrow.
|
|
255
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
256
|
+
if (/timeout|timed out/i.test(msg) && timeout !== undefined) {
|
|
257
|
+
throw new WaitForEventTimeoutError(eventName, timeout);
|
|
258
|
+
}
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
return ctx;
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Entrypoint handler — invoked by the generated WorkflowEntrypoint class.
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
async function entrypointHandler(event, step) {
|
|
269
|
+
const payload = event.payload;
|
|
270
|
+
if (!isEnvelope(payload)) {
|
|
271
|
+
throw new Error('Cloudflare Workflows adapter: workflow event payload missing __wfName/__wfInput envelope. ' +
|
|
272
|
+
'Workflows triggered through this adapter must be created via adapter.run() or adapter.runSchedule().');
|
|
273
|
+
}
|
|
274
|
+
const fn = registry.get(payload.__wfName);
|
|
275
|
+
if (!fn) {
|
|
276
|
+
throw new Error(`Cloudflare Workflows adapter: no workflow registered for name "${payload.__wfName}". ` +
|
|
277
|
+
'Call adapter.register(name, fn) before triggering it.');
|
|
278
|
+
}
|
|
279
|
+
const instanceId = event.instanceId ?? `cf-${Date.now()}`;
|
|
280
|
+
const ctx = buildContext(payload.__wfName, instanceId, payload.__wfInput, step);
|
|
281
|
+
return fn(ctx);
|
|
282
|
+
}
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Polling — wait for an instance to terminate.
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
async function pollUntilDone(instance) {
|
|
287
|
+
const start = Date.now();
|
|
288
|
+
// First check is unconditional so quick-completing workflows return
|
|
289
|
+
// without an extra delay tick.
|
|
290
|
+
let last = await instance.status();
|
|
291
|
+
while (true) {
|
|
292
|
+
if (last.status === 'complete')
|
|
293
|
+
return last.output;
|
|
294
|
+
if (last.status === 'errored' || last.status === 'terminated') {
|
|
295
|
+
const message = last.error?.message ?? `Workflow instance ${instance.id} ended with status ${last.status}`;
|
|
296
|
+
throw new DurableStepError(message, {
|
|
297
|
+
stepName: `<workflow:${instance.id}>`,
|
|
298
|
+
attempts: 1,
|
|
299
|
+
retryable: false,
|
|
300
|
+
cause: last.error ?? new Error(message),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
if (Date.now() - start > pollTimeoutMs) {
|
|
304
|
+
throw new DurableStepError(`Workflow instance ${instance.id} did not complete within ${pollTimeoutMs}ms`, {
|
|
305
|
+
stepName: `<workflow:${instance.id}>`,
|
|
306
|
+
attempts: 1,
|
|
307
|
+
retryable: false,
|
|
308
|
+
cause: new Error('poll timeout'),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
await delay(pollIntervalMs);
|
|
312
|
+
last = await instance.status();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// Public surface
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
function register(name, fn) {
|
|
319
|
+
registry.set(name, fn);
|
|
320
|
+
}
|
|
321
|
+
async function run(name, fn, input) {
|
|
322
|
+
// Implicitly register if the caller supplied a body. Allows the
|
|
323
|
+
// canonical port shape `dx.run(name, fn, input)` without a separate
|
|
324
|
+
// register() step. If the same name is later run() with no body, the
|
|
325
|
+
// most recent registration wins — matching the in-process adapter.
|
|
326
|
+
if (typeof fn === 'function') {
|
|
327
|
+
registry.set(name, fn);
|
|
328
|
+
}
|
|
329
|
+
const binding = resolveBinding();
|
|
330
|
+
const envelope = { __wfName: name, __wfInput: input };
|
|
331
|
+
const instance = await binding.create({ params: envelope });
|
|
332
|
+
if (!waitForCompletion) {
|
|
333
|
+
// Caller manages the instance; return it cast through unknown.
|
|
334
|
+
return instance;
|
|
335
|
+
}
|
|
336
|
+
return (await pollUntilDone(instance));
|
|
337
|
+
}
|
|
338
|
+
async function step(name, configOrFn, maybeFn) {
|
|
339
|
+
// Outside a workflow body CF Workflows have no step concept — the binding
|
|
340
|
+
// can't run a single step in isolation. We mirror the in-memory stub's
|
|
341
|
+
// behaviour for callers using `dx.step()` outside a `run`: execute the
|
|
342
|
+
// function directly without memoization. Inside a body, the
|
|
343
|
+
// {@link WorkflowContext.step} delegate is used (which translates to
|
|
344
|
+
// `step.do`).
|
|
345
|
+
void name;
|
|
346
|
+
const fn = typeof configOrFn === 'function' ? configOrFn : maybeFn;
|
|
347
|
+
return fn();
|
|
348
|
+
}
|
|
349
|
+
async function sleep(duration) {
|
|
350
|
+
// Outside a body, sleep is just a delay — there's no CF step context to
|
|
351
|
+
// suspend. This matches the in-memory stub.
|
|
352
|
+
const ms = typeof duration === 'number' ? duration : parseDurationLoose(duration);
|
|
353
|
+
if (ms > 0)
|
|
354
|
+
await delay(ms);
|
|
355
|
+
}
|
|
356
|
+
async function sleepUntil(date) {
|
|
357
|
+
const ms = date.getTime() - Date.now();
|
|
358
|
+
if (ms > 0)
|
|
359
|
+
await delay(ms);
|
|
360
|
+
}
|
|
361
|
+
function waitForEvent(name, timeout) {
|
|
362
|
+
// Outside a body there is no CF step.waitForEvent surface. Reject so
|
|
363
|
+
// callers don't accidentally rely on hibernation-style waits without a
|
|
364
|
+
// running workflow.
|
|
365
|
+
return Promise.reject(new Error(`Cloudflare Workflows adapter: waitForEvent("${name}"${timeout !== undefined ? `, ${String(timeout)}` : ''}) ` +
|
|
366
|
+
'is only supported inside a workflow body (ctx.waitForEvent). ' +
|
|
367
|
+
'Trigger the workflow via adapter.run() and call waitForEvent on its ctx.'));
|
|
368
|
+
}
|
|
369
|
+
function schedule(name, cron, fn) {
|
|
370
|
+
schedules.set(name, { cron, fn: fn });
|
|
371
|
+
registry.set(name, fn);
|
|
372
|
+
return {
|
|
373
|
+
id: name,
|
|
374
|
+
unsubscribe() {
|
|
375
|
+
schedules.delete(name);
|
|
376
|
+
// Leave the body in registry so already-fired runs still resolve.
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function defineSchedule(name, cron, fn) {
|
|
381
|
+
return schedule(name, cron, fn);
|
|
382
|
+
}
|
|
383
|
+
async function runSchedule(name) {
|
|
384
|
+
const entry = schedules.get(name);
|
|
385
|
+
if (!entry) {
|
|
386
|
+
throw new Error(`Cloudflare Workflows adapter: no schedule registered for "${name}". ` +
|
|
387
|
+
'Call adapter.defineSchedule(name, cron, fn) before invoking runSchedule.');
|
|
388
|
+
}
|
|
389
|
+
return run(name, entry.fn, undefined);
|
|
390
|
+
}
|
|
391
|
+
const adapter = {
|
|
392
|
+
kind: 'cloudflare',
|
|
393
|
+
limits: CLOUDFLARE_WORKFLOWS_LIMITS,
|
|
394
|
+
register,
|
|
395
|
+
run: run,
|
|
396
|
+
step: step,
|
|
397
|
+
sleep,
|
|
398
|
+
sleepUntil,
|
|
399
|
+
waitForEvent,
|
|
400
|
+
schedule,
|
|
401
|
+
defineSchedule,
|
|
402
|
+
runSchedule,
|
|
403
|
+
entrypointHandler,
|
|
404
|
+
};
|
|
405
|
+
return adapter;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Generate a {@link WorkflowEntrypointLike} subclass bound to `adapter`.
|
|
409
|
+
*
|
|
410
|
+
* Users export the returned class from their worker module and reference it
|
|
411
|
+
* in `wrangler.jsonc` under `workflows[].class_name`. CF instantiates the
|
|
412
|
+
* class for each workflow run, calls its `run(event, step)`, and the adapter
|
|
413
|
+
* dispatches into the registered body.
|
|
414
|
+
*
|
|
415
|
+
* **Why a factory instead of a fixed export?** CF binds workflows by class
|
|
416
|
+
* name at deploy time — each binding needs its own class identity. The
|
|
417
|
+
* factory pattern lets users have multiple adapters/classes in one module
|
|
418
|
+
* (e.g. one per binding) without colliding.
|
|
419
|
+
*
|
|
420
|
+
* If `Base` is omitted, this module declares an internal abstract class with
|
|
421
|
+
* the signature CF expects. In production, callers SHOULD pass the real
|
|
422
|
+
* `WorkflowEntrypoint` from `cloudflare:workers` so CF's runtime magic (env
|
|
423
|
+
* injection, `[Rpc.__WORKFLOW_ENTRYPOINT_BRAND]`, etc.) is preserved:
|
|
424
|
+
*
|
|
425
|
+
* ```ts
|
|
426
|
+
* import { WorkflowEntrypoint } from 'cloudflare:workers'
|
|
427
|
+
* import { createCloudflareWorkflowsDurableExecution, createWorkflowEntrypoint } from 'ai-workflows/durable-execution'
|
|
428
|
+
*
|
|
429
|
+
* const dx = createCloudflareWorkflowsDurableExecution({ binding: () => env.WORKFLOW })
|
|
430
|
+
* export const MyWorkflow = createWorkflowEntrypoint(dx, WorkflowEntrypoint)
|
|
431
|
+
* ```
|
|
432
|
+
*
|
|
433
|
+
* In tests / Node environments where `cloudflare:workers` isn't available,
|
|
434
|
+
* call without `Base` and the adapter's dispatch logic is exercised against
|
|
435
|
+
* the structural fake.
|
|
436
|
+
*/
|
|
437
|
+
export function createWorkflowEntrypoint(adapter, Base) {
|
|
438
|
+
if (Base) {
|
|
439
|
+
return class extends Base {
|
|
440
|
+
async run(event, step) {
|
|
441
|
+
return adapter.entrypointHandler(event, step);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
// Pure-JS shim used when callers don't (or can't) supply the real CF base
|
|
446
|
+
// class. Sufficient for unit tests of the dispatch path; production callers
|
|
447
|
+
// should always pass `WorkflowEntrypoint` from `cloudflare:workers`.
|
|
448
|
+
return class WorkflowEntrypointShim {
|
|
449
|
+
constructor(_ctx, _env) {
|
|
450
|
+
// Match CF's two-arg constructor shape; we don't need the values.
|
|
451
|
+
void _ctx;
|
|
452
|
+
void _env;
|
|
453
|
+
}
|
|
454
|
+
async run(event, step) {
|
|
455
|
+
return adapter.entrypointHandler(event, step);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
// =============================================================================
|
|
460
|
+
// Internal: minimal duration parser (top-level sleep outside a body)
|
|
461
|
+
// =============================================================================
|
|
462
|
+
/**
|
|
463
|
+
* Tolerant duration parser used only by the top-level `sleep()` outside a
|
|
464
|
+
* body. Inside a body, durations are passed verbatim to CF's `step.sleep`,
|
|
465
|
+
* which has its own grammar. We do not constrain string forms here as
|
|
466
|
+
* tightly as the in-memory stub since CF accepts a richer grammar
|
|
467
|
+
* (`'10 seconds'`, `'1 day'`, etc.).
|
|
468
|
+
*/
|
|
469
|
+
function parseDurationLoose(input) {
|
|
470
|
+
const trimmed = input.trim().toLowerCase();
|
|
471
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(ms|millisecond|milliseconds|s|sec|second|seconds|m|min|minute|minutes|h|hr|hour|hours|d|day|days|w|week|weeks)$/);
|
|
472
|
+
if (!match) {
|
|
473
|
+
throw new Error(`Cloudflare Workflows adapter: unrecognised duration "${input}"`);
|
|
474
|
+
}
|
|
475
|
+
const value = parseFloat(match[1]);
|
|
476
|
+
const unit = match[2];
|
|
477
|
+
switch (unit) {
|
|
478
|
+
case 'ms':
|
|
479
|
+
case 'millisecond':
|
|
480
|
+
case 'milliseconds':
|
|
481
|
+
return value;
|
|
482
|
+
case 's':
|
|
483
|
+
case 'sec':
|
|
484
|
+
case 'second':
|
|
485
|
+
case 'seconds':
|
|
486
|
+
return value * 1000;
|
|
487
|
+
case 'm':
|
|
488
|
+
case 'min':
|
|
489
|
+
case 'minute':
|
|
490
|
+
case 'minutes':
|
|
491
|
+
return value * 60_000;
|
|
492
|
+
case 'h':
|
|
493
|
+
case 'hr':
|
|
494
|
+
case 'hour':
|
|
495
|
+
case 'hours':
|
|
496
|
+
return value * 3_600_000;
|
|
497
|
+
case 'd':
|
|
498
|
+
case 'day':
|
|
499
|
+
case 'days':
|
|
500
|
+
return value * 86_400_000;
|
|
501
|
+
case 'w':
|
|
502
|
+
case 'week':
|
|
503
|
+
case 'weeks':
|
|
504
|
+
return value * 604_800_000;
|
|
505
|
+
/* istanbul ignore next */
|
|
506
|
+
default:
|
|
507
|
+
throw new Error(`Cloudflare Workflows adapter: unrecognised duration unit "${unit}"`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
//# sourceMappingURL=durable-execution-cloudflare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-execution-cloudflare.js","sourceRoot":"","sources":["../src/durable-execution-cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsJG;AAEH,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GAOzB,MAAM,wBAAwB,CAAA;AA+J/B,MAAM,2BAA2B,GAA8B;IAC7D,QAAQ,EAAE,MAAM;IAChB,sBAAsB,EAAE,MAAM;IAC9B,YAAY,EAAE,GAAG;IACjB,eAAe,EAAE,SAAS;CAC3B,CAAA;AAoBD,SAAS,UAAU,CAAC,OAAgB;IAClC,OAAO,CACL,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,KAAK,IAAI;QAChB,UAAU,IAAI,OAAO;QACrB,OAAQ,OAAkC,CAAC,QAAQ,KAAK,QAAQ;QAChE,WAAW,IAAI,OAAO,CACvB,CAAA;AACH,CAAC;AAiED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,yCAAyC,CACvD,OAAmD;IAEnD,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,IAAI,CAAA;IAC3D,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,GAAG,CAAA;IACpD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IAClE,MAAM,KAAK,GACT,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IAE5F,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAsB,CAAA;IAC9C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgE,CAAA;IAEzF,SAAS,cAAc;QACrB,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAA;QACzB,OAAO,OAAO,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1C,CAAC;IAED,8EAA8E;IAC9E,0EAA0E;IAC1E,2EAA2E;IAC3E,8EAA8E;IAE9E,SAAS,YAAY,CACnB,IAAY,EACZ,UAAkB,EAClB,KAAa,EACb,IAAsB;QAEtB,0EAA0E;QAC1E,uEAAuE;QACvE,sEAAsE;QACtE,kBAAkB;QAClB,IAAI,YAAY,GAAG,CAAC,CAAA;QACpB,IAAI,iBAAiB,GAAG,CAAC,CAAA;QACzB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAA;QAE9C,MAAM,GAAG,GAA4B;YACnC,KAAK;YACL,UAAU;YACV,IAAI;YACJ,IAAI,EAAE,CAAC,CACL,QAAgB,EAChB,UAAiD,EACjD,OAAgC,EAChC,EAAE;gBACF,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;oBACrC,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAqB,CAAA;gBAC1D,CAAC;gBACD,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAoC,EAAE,OAAQ,CAAqB,CAAA;YAC9F,CAAC,CAAoC;YACrC,KAAK,CAAC,KAAK,CAAC,QAAQ;gBAClB,MAAM,QAAQ,GAAG,YAAY,EAAE,YAAY,EAAE,CAAA;gBAC7C,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,QAA2B,CAAC,CAAA;YACzD,CAAC;YACD,KAAK,CAAC,UAAU,CAAC,IAAI;gBACnB,MAAM,QAAQ,GAAG,iBAAiB,EAAE,iBAAiB,EAAE,CAAA;gBACvD,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YACvC,CAAC;YACD,KAAK,CAAC,YAAY,CAAc,SAAiB,EAAE,OAAyB;gBAC1E,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;gBAClD,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;gBAChC,MAAM,QAAQ,GAAG,mBAAmB,SAAS,KAAK,GAAG,EAAE,CAAA;gBACvD,IAAI,CAAC;oBACH,MAAM,IAAI,GAAgD,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;oBAC7E,IAAI,OAAO,KAAK,SAAS;wBAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;oBACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAI,QAAQ,EAAE,IAAI,CAAC,CAAA;oBACzD,sEAAsE;oBACtE,8DAA8D;oBAC9D,IACE,MAAM,KAAK,IAAI;wBACf,OAAO,MAAM,KAAK,QAAQ;wBAC1B,SAAS,IAAK,MAAkC,EAChD,CAAC;wBACD,OAAQ,MAAyB,CAAC,OAAO,CAAA;oBAC3C,CAAC;oBACD,OAAO,MAAW,CAAA;gBACpB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,+DAA+D;oBAC/D,+DAA+D;oBAC/D,8BAA8B;oBAC9B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oBAC5D,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;wBAC5D,MAAM,IAAI,wBAAwB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;oBACxD,CAAC;oBACD,MAAM,GAAG,CAAA;gBACX,CAAC;YACH,CAAC;SACF,CAAA;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,8EAA8E;IAC9E,0EAA0E;IAC1E,8EAA8E;IAE9E,KAAK,UAAU,iBAAiB,CAC9B,KAAwB,EACxB,IAAsB;QAEtB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA;QAC7B,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,4FAA4F;gBAC1F,sGAAsG,CACzG,CAAA;QACH,CAAC;QACD,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,KAAK,CACb,kEAAkE,OAAO,CAAC,QAAQ,KAAK;gBACrF,uDAAuD,CAC1D,CAAA;QACH,CAAC;QACD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;QACzD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;QAC/E,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;IAChB,CAAC;IAED,8EAA8E;IAC9E,+CAA+C;IAC/C,8EAA8E;IAE9E,KAAK,UAAU,aAAa,CAAC,QAA8B;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,oEAAoE;QACpE,+BAA+B;QAC/B,IAAI,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAA;QAClC,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;gBAAE,OAAO,IAAI,CAAC,MAAM,CAAA;YAClD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;gBAC9D,MAAM,OAAO,GACX,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,qBAAqB,QAAQ,CAAC,EAAE,sBAAsB,IAAI,CAAC,MAAM,EAAE,CAAA;gBAC5F,MAAM,IAAI,gBAAgB,CAAC,OAAO,EAAE;oBAClC,QAAQ,EAAE,aAAa,QAAQ,CAAC,EAAE,GAAG;oBACrC,QAAQ,EAAE,CAAC;oBACX,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC;iBACxC,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,aAAa,EAAE,CAAC;gBACvC,MAAM,IAAI,gBAAgB,CACxB,qBAAqB,QAAQ,CAAC,EAAE,4BAA4B,aAAa,IAAI,EAC7E;oBACE,QAAQ,EAAE,aAAa,QAAQ,CAAC,EAAE,GAAG;oBACrC,QAAQ,EAAE,CAAC;oBACX,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,IAAI,KAAK,CAAC,cAAc,CAAC;iBACjC,CACF,CAAA;YACH,CAAC;YACD,MAAM,KAAK,CAAC,cAAc,CAAC,CAAA;YAC3B,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAA;QAChC,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,iBAAiB;IACjB,8EAA8E;IAE9E,SAAS,QAAQ,CACf,IAAY,EACZ,EAA+B;QAE/B,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAgB,CAAC,CAAA;IACtC,CAAC;IAED,KAAK,UAAU,GAAG,CAChB,IAAY,EACZ,EAA+B,EAC/B,KAAa;QAEb,gEAAgE;QAChE,oEAAoE;QACpE,qEAAqE;QACrE,mEAAmE;QACnE,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;YAC7B,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAgB,CAAC,CAAA;QACtC,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;QAChC,MAAM,QAAQ,GAA6B,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;QAC/E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC3D,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,+DAA+D;YAC/D,OAAO,QAA8B,CAAA;QACvC,CAAC;QACD,OAAO,CAAC,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAY,CAAA;IACnD,CAAC;IAED,KAAK,UAAU,IAAI,CACjB,IAAY,EACZ,UAA2C,EAC3C,OAA0B;QAE1B,0EAA0E;QAC1E,uEAAuE;QACvE,uEAAuE;QACvE,4DAA4D;QAC5D,qEAAqE;QACrE,cAAc;QACd,KAAK,IAAI,CAAA;QACT,MAAM,EAAE,GAAG,OAAO,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAQ,CAAA;QACnE,OAAO,EAAE,EAAE,CAAA;IACb,CAAC;IAED,KAAK,UAAU,KAAK,CAAC,QAAyB;QAC5C,wEAAwE;QACxE,4CAA4C;QAC5C,MAAM,EAAE,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QACjF,IAAI,EAAE,GAAG,CAAC;YAAE,MAAM,KAAK,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,UAAU,UAAU,CAAC,IAAU;QAClC,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtC,IAAI,EAAE,GAAG,CAAC;YAAE,MAAM,KAAK,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,SAAS,YAAY,CAAc,IAAY,EAAE,OAAyB;QACxE,qEAAqE;QACrE,uEAAuE;QACvE,oBAAoB;QACpB,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CACP,+CAA+C,IAAI,IACjD,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EACnD,IAAI;YACF,+DAA+D;YAC/D,0EAA0E,CAC7E,CACF,CAAA;IACH,CAAC;IAED,SAAS,QAAQ,CACf,IAAY,EACZ,IAAY,EACZ,EAAkC;QAElC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAoC,EAAE,CAAC,CAAA;QACvE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAgB,CAAC,CAAA;QACpC,OAAO;YACL,EAAE,EAAE,IAAI;YACR,WAAW;gBACT,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACtB,kEAAkE;YACpE,CAAC;SACF,CAAA;IACH,CAAC;IAED,SAAS,cAAc,CACrB,IAAY,EACZ,IAAY,EACZ,EAAkC;QAElC,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;IAED,KAAK,UAAU,WAAW,CAAC,IAAY;QACrC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACjC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,6DAA6D,IAAI,KAAK;gBACpE,0EAA0E,CAC7E,CAAA;QACH,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;IACvC,CAAC;IAED,MAAM,OAAO,GAAwC;QACnD,IAAI,EAAE,YAAmD;QACzD,MAAM,EAAE,2BAA2B;QACnC,QAAQ;QACR,GAAG,EAAE,GAAqC;QAC1C,IAAI,EAAE,IAAuC;QAC7C,KAAK;QACL,UAAU;QACV,YAAY;QACZ,QAAQ;QACR,cAAc;QACd,WAAW;QACX,iBAAiB;KAClB,CAAA;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAA4C,EAC5C,IAAoC;IAEpC,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,KAAM,SAAQ,IAAI;YACd,KAAK,CAAC,GAAG,CAAC,KAA2B,EAAE,IAAsB;gBACpE,OAAO,OAAO,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YAC/C,CAAC;SAC0C,CAAA;IAC/C,CAAC;IACD,0EAA0E;IAC1E,4EAA4E;IAC5E,qEAAqE;IACrE,OAAO,MAAM,sBAAsB;QACjC,YAAY,IAAa,EAAE,IAAa;YACtC,kEAAkE;YAClE,KAAK,IAAI,CAAA;YACT,KAAK,IAAI,CAAA;QACX,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,KAA2B,EAAE,IAAsB;YAC3D,OAAO,OAAO,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC/C,CAAC;KAC0C,CAAA;AAC/C,CAAC;AAED,gFAAgF;AAChF,qEAAqE;AACrE,gFAAgF;AAEhF;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,KAAa;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CACzB,qIAAqI,CACtI,CAAA;IACD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,wDAAwD,KAAK,GAAG,CAAC,CAAA;IACnF,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;IACnC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;IACtB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,IAAI,CAAC;QACV,KAAK,aAAa,CAAC;QACnB,KAAK,cAAc;YACjB,OAAO,KAAK,CAAA;QACd,KAAK,GAAG,CAAC;QACT,KAAK,KAAK,CAAC;QACX,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,KAAK,GAAG,IAAI,CAAA;QACrB,KAAK,GAAG,CAAC;QACT,KAAK,KAAK,CAAC;QACX,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,KAAK,GAAG,MAAM,CAAA;QACvB,KAAK,GAAG,CAAC;QACT,KAAK,IAAI,CAAC;QACV,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,KAAK,GAAG,SAAS,CAAA;QAC1B,KAAK,GAAG,CAAC;QACT,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,OAAO,KAAK,GAAG,UAAU,CAAA;QAC3B,KAAK,GAAG,CAAC;QACT,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,KAAK,GAAG,WAAW,CAAA;QAC5B,0BAA0B;QAC1B;YACE,MAAM,IAAI,KAAK,CAAC,6DAA6D,IAAI,GAAG,CAAC,CAAA;IACzF,CAAC;AACH,CAAC"}
|