Skip to content

Commit 24d2e93

Browse files
authored
chore: Standardize cf workbench integration (#15)
Reorganizing a lot of things within the workbench mainly to reduce slop code and maintaining a single source of truth. - Standardized both benches on `GET/POST /api/workflows/:key` - Vite now owns complete Sideffect behavior coverage `workflows.e2e.test.ts` - TanStack covers framework delegation, API exposure, and native workflow coexistence: `integration.e2e.test.ts` - Discovery/build coverage is separated in each bench - Shared only mechanical server/polling code in `workflow-http.ts` - Renamed shared case metadata to `workflow-cases.ts` - Native workflow implementations remain explicit in both hosts intentionally (due to the requirement of bindings being unique in a Cloudflare Account) - Improve UI feedback for workflows in Tanstack Bench
1 parent fb8ff54 commit 24d2e93

28 files changed

Lines changed: 1428 additions & 785 deletions

bun.lock

Lines changed: 0 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sideffect/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type {
1616
WorkflowContext,
1717
WorkflowDefinition,
1818
WorkflowEvent,
19+
WorkflowInput,
1920
WorkflowLayer,
2021
WorkflowRun,
2122
} from "./types.ts";

packages/sideffect/src/types.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,13 @@ export type WorkflowRun<Payload, Result, Env = DefaultCloudflareEnv> = (
183183
) => MaybeEffect<Result>;
184184

185185
/** Sideffect workflow definition created by `Workflow.make(...)`. */
186-
export interface WorkflowDefinition<Payload, Env = DefaultCloudflareEnv> {
186+
export interface WorkflowDefinition<Payload, Env = DefaultCloudflareEnv, Input = Payload> {
187187
/** Runtime tag used by Sideffect to validate workflow definitions. */
188188
readonly _tag: "WorkflowDefinition";
189189
/** Cloudflare Workflow name. */
190190
readonly name: string;
191191
/** Schema used to decode incoming workflow event payloads. */
192-
readonly payloadSchema: Schema.Schema<Payload>;
192+
readonly payloadSchema: Schema.Codec<Payload, Input, never, unknown>;
193193
/**
194194
* Binds a workflow definition to its implementation.
195195
*
@@ -198,21 +198,32 @@ export interface WorkflowDefinition<Payload, Env = DefaultCloudflareEnv> {
198198
*/
199199
toLayer<NextResult>(
200200
run: WorkflowRun<Payload, NextResult, Env>,
201-
): WorkflowLayer<Payload, NextResult, Env>;
201+
): WorkflowLayer<Payload, NextResult, Env, Input>;
202202
}
203203

204204
/** Runnable Sideffect workflow layer. */
205-
export interface WorkflowLayer<Payload, Result = unknown, Env = DefaultCloudflareEnv> {
205+
export interface WorkflowLayer<
206+
Payload,
207+
Result = unknown,
208+
Env = DefaultCloudflareEnv,
209+
Input = Payload,
210+
> {
206211
/** Runtime tag used by Sideffect to validate workflow layers. */
207212
readonly _tag: "WorkflowLayer";
208213
/** Workflow definition and payload schema. */
209-
readonly workflow: WorkflowDefinition<Payload, Env>;
214+
readonly workflow: WorkflowDefinition<Payload, Env, Input>;
210215
/** Workflow implementation. */
211216
readonly run: WorkflowRun<Payload, Result, Env>;
212217
}
213218

219+
/** Extracts the encoded input accepted by a Sideffect workflow binding. */
220+
export type WorkflowInput<Layer> =
221+
Layer extends WorkflowLayer<infer _Payload, infer _Result, infer _Env, infer Input>
222+
? Input
223+
: never;
224+
214225
/** @internal Any Sideffect workflow layer. */
215-
export type WorkflowLayerAny = WorkflowLayer<any, any, any>;
226+
export type WorkflowLayerAny = WorkflowLayer<any, any, any, any>;
216227

217228
/** @internal Named workflow layers used to generate Cloudflare entrypoints. */
218229
export type WorkflowLayerEntries = Record<string, WorkflowLayerAny>;

packages/sideffect/src/vite/generated-entry.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,23 +112,22 @@ function generateWorkflowEnvTypes(
112112
.join("\n");
113113
const envBindings = workflows
114114
.map((workflow, index) => {
115-
const payload = `__SideffectWorkflowPayload<typeof __SideffectWorkflow${index}>`;
115+
const payload = `WorkflowInput<typeof __SideffectWorkflow${index}>`;
116116
return ` ${workflow.config.binding}: __SideffectCloudflareWorkflow<${payload}>;`;
117117
})
118118
.join("\n");
119119
const cloudflareEnvBindings = workflows
120120
.map((workflow, index) => {
121-
const payload = `__SideffectWorkflowPayload<typeof __SideffectWorkflow${index}>`;
121+
const payload = `WorkflowInput<typeof __SideffectWorkflow${index}>`;
122122
return ` ${workflow.config.binding}: __SideffectCloudflareWorkflow<${payload}>;`;
123123
})
124124
.join("\n");
125125

126126
return `// Generated by Sideffect. Do not edit.
127-
import type { WorkflowLayer } from "sideffect";
127+
import type { WorkflowInput } from "sideffect";
128128
${imports}
129129
130130
type __SideffectCloudflareWorkflow<Payload> = Workflow<Payload>;
131-
type __SideffectWorkflowPayload<T> = T extends WorkflowLayer<infer Payload, any> ? Payload : never;
132131
133132
declare global {
134133
interface Env {

packages/sideffect/src/workflow.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import type {
88
} from "./types.ts";
99

1010
/** Options for creating a Sideffect workflow definition. */
11-
export interface WorkflowMakeOptions<Payload> {
11+
export interface WorkflowMakeOptions<Payload, Input = Payload> {
1212
/** Cloudflare Workflow name. */
1313
readonly name: string;
1414
/** Schema used to decode incoming workflow event payloads. */
15-
readonly payload: Schema.Schema<Payload>;
15+
readonly payload: Schema.Codec<Payload, Input, never, unknown>;
1616
}
1717

1818
/** Helpers for defining Sideffect workflows. */
@@ -35,14 +35,16 @@ export const Workflow = {
3535
* });
3636
* ```
3737
*/
38-
make<Payload, Env = DefaultCloudflareEnv>(
39-
options: WorkflowMakeOptions<Payload>,
40-
): WorkflowDefinition<Payload, Env> {
41-
const definition: WorkflowDefinition<Payload, Env> = {
38+
make<Payload, Env = DefaultCloudflareEnv, Input = Payload>(
39+
options: WorkflowMakeOptions<Payload, Input>,
40+
): WorkflowDefinition<Payload, Env, Input> {
41+
const definition: WorkflowDefinition<Payload, Env, Input> = {
4242
_tag: "WorkflowDefinition",
4343
name: options.name,
4444
payloadSchema: options.payload,
45-
toLayer<Result>(run: WorkflowRun<Payload, Result, Env>): WorkflowLayer<Payload, Result, Env> {
45+
toLayer<Result>(
46+
run: WorkflowRun<Payload, Result, Env>,
47+
): WorkflowLayer<Payload, Result, Env, Input> {
4648
return {
4749
_tag: "WorkflowLayer",
4850
workflow: definition,

packages/sideffect/tests/ctx-env.types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Rollback, Schema, Step, Workflow } from "../src/index.ts";
2+
import type { WorkflowInput } from "../src/index.ts";
23

34
declare global {
45
namespace Cloudflare {
@@ -41,4 +42,12 @@ Workflow.make({
4142
return step.do(envBackedStep, workflow.payload);
4243
});
4344

45+
const encodedWorkflow = Workflow.make({
46+
name: "encoded-workflow",
47+
payload: Schema.NumberFromString,
48+
}).toLayer(async ({ payload }) => payload);
49+
50+
const encodedInput: WorkflowInput<typeof encodedWorkflow> = "42";
51+
void encodedInput;
52+
4453
export {};

packages/sideffect/tests/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ test("Sideffect workflows plugin discovers workflows from Vite root before confi
759759
const envTypes = readFileSync(join(root, "sideffect-env.d.ts"), "utf8");
760760
expect(envTypes).not.toContain("Workflow as CloudflareWorkflow");
761761
expect(envTypes).not.toContain('from "cloudflare:workers"');
762+
expect(envTypes).toContain('import type { WorkflowInput } from "sideffect";');
762763
expect(envTypes).toContain(
763764
"type __SideffectCloudflareWorkflow<Payload> = Workflow<Payload>;",
764765
);

0 commit comments

Comments
 (0)