WDK Integrations: One Pattern, Eight Frameworks
Workflow DevKit (WDK), which launched at Ship AI over a month ago, now supports eight frameworks including SvelteKit, Astro, Express, and Hono—with TanStack Start and React Router in active development. The integrations may look different from the outside—Next.js, Express, and SvelteKit each have their own bundlers, routing, and DX—but under the hood, every integration follows the same two-phase approach.
Phase One: Build-Time Handler Generation
The build phase compiles your workflow and step functions into executable handler files. It handles bundling, output paths, and framework-specific compatibility patches, while setting up hot module replacement so changes appear instantly without restarting the dev server.
Phase Two: Runtime Endpoint Exposure
At runtime, the integration applies workflow client transforms and makes the generated handler files reachable by your application's server. Workflows become available over HTTP without manual endpoint setup. The specifics differ by framework, but the sequence is consistent.
The SWC Compiler Plugin's Three Modes
The key to this flexibility is WDK's SWC plugin, which transforms the same source file into different outputs depending on mode:
- Client mode runs during the framework build via a Rollup or Vite plugin, converting workflow calls into HTTP client code and adding
workflowIdproperties. - Step mode runs during WDK's esbuild phase, turning
"use step"functions into server-executed HTTP handlers. - Workflow mode also runs during esbuild, converting
"use workflow"functions into sandboxed virtual-environment orchestrators.
Write your code once; the compiler generates the client, step handler, and workflow handler automatically.
SvelteKit: A Concrete Example
The SvelteKit integration is Vite-based with file-based routing. Setup takes a single line in vite.config.ts:
import { sveltekit } from "@sveltejs/kit/vite";
import { workflowPlugin } from "workflow/sveltekit";
export default {
plugins: [
sveltekit(),
workflowPlugin()
]
};
Behind the scenes, workflowPlugin() executes both phases.
Build-time work happens in parallel:
- Client transformation: The
workflowTransformPlugin()from@workflow/rolluphooks into Vite's build and uses SWC in client mode to transformstart(myWorkflow, [...])calls, adding anidproperty to workflows. - Handler generation: The
SvelteKitBuildercreates two esbuild bundles—one for steps (mode: 'step'), one for workflows (mode: 'workflow')—which become+server.jsfiles insrc/routes/.well-known/workflow/v1.
Runtime is automatic. SvelteKit's file-based router discovers the generated files and exposes them as HTTP endpoints, as long as they're named +server.js. No manual wiring.
Because many frameworks share Vite's plugin system, HMR, and file-based routing, this pattern transfers readily. The Astro integration, for example, is nearly identical to SvelteKit's—only route output paths and compatibility patches differ.
For frameworks without a bundler—Express, Hono—WDK uses Nitro. This server toolkit provides file-based routing, a build system, and quality-of-life features like virtual handlers mounted at runtime, enabling bare HTTP servers to gain the same workflow capabilities.
Handling Framework Request Objects
Framework differences around the concept of a "request" surfaced quickly during multi-framework work. SvelteKit, for instance, passes a custom request object to route handlers, while WDK handlers expect the standard Web Request API. The fix was to inject a small converter function into each generated handler:
async function convertSvelteKitRequest(request) {
const options = {
method: request.method,
headers: new Headers(request.headers)
};
if (!['GET', 'HEAD'].includes(request.method)) {
options.body = await request.arrayBuffer();
};
return new Request(request.url, options);
};
This helper is embedded in every generated workflow handler file to ensure SvelteKit compatibility.
Hot Module Replacement in Practice
When you save a workflow file in SvelteKit, three steps occur:
- Vite's
hotUpdatehook fires with the changed file. - WDK checks for
"use workflow"or"use step"directives. - If found, an esbuild rebuild triggers.
async hotUpdate({ file, read }) {
const content = await read();
const useWorkflowPattern = /^\s*(['"])use workflow\1;?\s*$/m;
const useStepPattern = /^\s*(['"])use step\1;?\s*$/m;
if (!useWorkflowPattern.test(content) && !useStepPattern.test(content)) {
return; // Not a workflow file, let Vite handle normally
}
await enqueue(() => builder.build()); // Queue rebuild with esbuild: important if concurrent builds ever happen
};
Two Framework Categories Emerge
Building multiple integrations revealed two distinct framework archetypes:
- File-based routing frameworks (Next.js, SvelteKit, Nuxt): The build phase outputs handler files to framework-specific directories (
app/.well-known/workflow/v1for Next.js,src/routes/.well-known/workflow/v1for SvelteKit), which the framework auto-discovers as HTTP endpoints. Each framework requires different patches for endpoint definitions and handling. - HTTP server frameworks (Express, Hono): Without a built-in bundler, WDK uses esbuild to bundle workflows, then Nitro mounts them as virtual handlers that wrap the HTTP server at runtime, exposing the workflow endpoints.
The prevalence of Vite-based frameworks meant much of the integration code—plugin registration, HMR configuration, client transforms—was once written and shared across SvelteKit, Astro, and Nuxt, with adaptations only for routing patterns.
This pattern held through all six additional integrations since launch, with WDK attracting over 1,300 GitHub stars. What appears as six distinct framework problems is really one problem—build-time generation, runtime registration, a few framework-specific details—solved consistently. The takeaway for developers: durable workflows are available in whatever framework they already use, without migration or extra infrastructure.



