Skip to content

Commit 244aa57

Browse files
Workflows binding (#7012)
* start from workflows branch, squashed * add Workflow support to `wrangler types` * update fixture * add (skipped) e2e test * mark validation as TODO + remove commented code Co-authored-by: Luís Duarte <[email protected]>
1 parent 1f6ff8b commit 244aa57

35 files changed

+457
-7
lines changed

.changeset/green-parrots-lick.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Add support for Workflow bindings (in deployments, not yet in local dev)
6+
7+
To bind to a workflow, add a `workflows` section in your wrangler.toml:
8+
9+
```toml
10+
[[workflows]]
11+
binding = "WORKFLOW"
12+
name = "my-workflow"
13+
class_name = "MyDemoWorkflow"
14+
```
15+
16+
and export an entrypoint (e.g. `MyDemoWorkflow`) in your script:
17+
18+
```typescript
19+
import { WorkflowEntrypoint } from "cloudflare:workers";
20+
21+
export class MyDemoWorkflow extends WorkflowEntrypoint<Env, Params> {...}
22+
```

fixtures/workflow/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "my-workflow",
3+
"private": true,
4+
"scripts": {
5+
"deploy": "wrangler deploy",
6+
"start": "wrangler dev --x-dev-env"
7+
},
8+
"devDependencies": {
9+
"@cloudflare/workers-types": "^4.20241011.0",
10+
"wrangler": "workspace:*"
11+
},
12+
"volta": {
13+
"extends": "../../package.json"
14+
}
15+
}

fixtures/workflow/src/index.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {
2+
WorkerEntrypoint,
3+
Workflow,
4+
WorkflowEvent,
5+
WorkflowStep,
6+
} from "cloudflare:workers";
7+
8+
type Params = {
9+
name: string;
10+
};
11+
export class Demo extends Workflow<{}, Params> {
12+
async run(events: Array<WorkflowEvent<Params>>, step: WorkflowStep) {
13+
const { timestamp, payload } = events[0];
14+
const result = await step.do("First step", async function () {
15+
return {
16+
output: "First step result",
17+
};
18+
});
19+
20+
await step.sleep("Wait", "1 minute");
21+
22+
const result2 = await step.do("Second step", async function () {
23+
return {
24+
output: "Second step result",
25+
};
26+
});
27+
28+
return {
29+
result,
30+
result2,
31+
timestamp,
32+
payload,
33+
};
34+
}
35+
}
36+
37+
type Env = {
38+
WORKFLOW: {
39+
create: (id: string) => {
40+
pause: () => {};
41+
};
42+
};
43+
};
44+
export default class extends WorkerEntrypoint<Env> {
45+
async fetch() {
46+
const handle = await this.env.WORKFLOW.create(crypto.randomUUID());
47+
await handle.pause();
48+
return new Response();
49+
}
50+
}

fixtures/workflow/tsconfig.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es2021",
4+
"lib": ["es2021"],
5+
"module": "es2022",
6+
"types": ["@cloudflare/workers-types/experimental"],
7+
"noEmit": true,
8+
"isolatedModules": true,
9+
"forceConsistentCasingInFileNames": true,
10+
"strict": true,
11+
"skipLibCheck": true
12+
}
13+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Generated by Wrangler by running `wrangler types`
2+
3+
interface Env {
4+
WORKFLOW: Workflow;
5+
}

fixtures/workflow/wrangler.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#:schema node_modules/wrangler/config-schema.json
2+
name = "my-workflow-demo"
3+
main = "src/index.ts"
4+
compatibility_date = "2024-10-11"
5+
6+
[[workflows]]
7+
binding = "WORKFLOW"
8+
name = "my-workflow"
9+
class_name = "Demo"

packages/wrangler/e2e/dev-with-resources.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,47 @@ describe.sequential.each(RUNTIMES)("Bindings: $flags", ({ runtime, flags }) => {
528528
await worker.readUntil(//);
529529
});
530530

531+
// TODO: enable for remove dev once realish preview supports it
532+
// TODO: enable for local dev once implemented
533+
it.skip("exposes Workflow bindings", async () => {
534+
await helper.seed({
535+
"wrangler.toml": dedent`
536+
name = "my-workflow-demo"
537+
main = "src/index.ts"
538+
compatibility_date = "2024-10-11"
539+
540+
[[workflows]]
541+
binding = "WORKFLOW"
542+
name = "my-workflow"
543+
class_name = "Demo"
544+
`,
545+
"src/index.ts": dedent`
546+
import { WorkflowEntrypoint } from "cloudflare:workers";
547+
548+
export default {
549+
async fetch(request, env, ctx) {
550+
if (env.WORKFLOW === undefined) {
551+
return new Response("env.WORKFLOW is undefined");
552+
}
553+
554+
return new Response("env.WORKFLOW is available");
555+
}
556+
}
557+
558+
export class Demo extends WorkflowEntrypoint {
559+
run() {
560+
// blank
561+
}
562+
}
563+
`,
564+
});
565+
const worker = helper.runLongLived(`wrangler dev ${flags}`);
566+
const { url } = await worker.waitForReady();
567+
const res = await fetch(url);
568+
569+
await expect(res.text()).resolves.toBe("env.WORKFLOW is available");
570+
});
571+
531572
// TODO(soon): implement E2E tests for other bindings
532573
it.todo("exposes hyperdrive bindings");
533574
it.skipIf(isLocal).todo("exposes send email bindings");

packages/wrangler/src/__tests__/configuration.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ describe("normalizeAndValidateConfig()", () => {
131131
placement: undefined,
132132
tail_consumers: undefined,
133133
pipelines: [],
134+
workflows: [],
134135
});
135136
expect(diagnostics.hasErrors()).toBe(false);
136137
expect(diagnostics.hasWarnings()).toBe(false);

packages/wrangler/src/__tests__/navigator-user-agent.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ describe("defineNavigatorUserAgent is respected", () => {
116116
serveLegacyAssetsFromWorker: false,
117117
mockAnalyticsEngineDatasets: [],
118118
doBindings: [],
119+
workflowBindings: [],
119120
define: {},
120121
alias: {},
121122
checkFetch: false,
@@ -175,6 +176,7 @@ describe("defineNavigatorUserAgent is respected", () => {
175176
moduleCollector: noopModuleCollector,
176177
serveLegacyAssetsFromWorker: false,
177178
doBindings: [],
179+
workflowBindings: [],
178180
define: {},
179181
alias: {},
180182
mockAnalyticsEngineDatasets: [],

packages/wrangler/src/__tests__/type-generation.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ const bindingsConfigMock: Omit<
164164
},
165165
],
166166
},
167+
workflows: [],
167168
r2_buckets: [
168169
{
169170
binding: "R2_BUCKET_BINDING",

0 commit comments

Comments
 (0)