Skip to content

Commit f0c5331

Browse files
committed
feat: expose world helpers via workflow/api
1 parent 51e6d12 commit f0c5331

File tree

8 files changed

+898
-1
lines changed

8 files changed

+898
-1
lines changed

.changeset/expose-world-helpers.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"workflow": patch
3+
---
4+
5+
Expose `getWorld` plus every `World` helper via `workflow/api`, add delegation tests, and document how to access the singleton programmatically.

docs/content/docs/api-reference/workflow-api/index.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ Workflow DevKit provides runtime functions that are used outside of workflow and
2525
<Card href="/docs/api-reference/workflow-api/get-run" title="getRun()">
2626
Get workflow run status and metadata without waiting for completion.
2727
</Card>
28+
<Card href="/docs/api-reference/workflow-api/world" title="World helpers">
29+
Access the World singleton plus list/cancel APIs for runs, steps, events, and hooks.
30+
</Card>
2831
</Cards>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
title: World helpers
3+
---
4+
5+
# Accessing the World singleton
6+
7+
> “Is there any public API to access the active default World? I'd like to be able to list active runs in my app, like I can via the CLI. I could store them myself, but if there is an API for this that works across testing and deployment, that'd be even better!”
8+
9+
Yes. Import `getWorld` or any of the helper functions below directly from `workflow/api`. They proxy to the same singleton that powers the CLI, whether you're running locally with the embedded world, in tests (via `setWorld`), or on Vercel.
10+
11+
```typescript
12+
import {
13+
getWorld,
14+
listRuns,
15+
listSteps,
16+
cancelRun,
17+
queue,
18+
} from 'workflow/api';
19+
20+
const runs = await listRuns({ status: 'running', pagination: { limit: 10 } });
21+
await cancelRun(runs.data[0].runId);
22+
23+
// You still have full access to the underlying World instance when needed.
24+
const world = getWorld();
25+
await world.start?.();
26+
```
27+
28+
## Example: List and cancel runs in a Next.js route
29+
30+
```typescript filename="app/api/workflows/route.ts"
31+
import { listRuns, cancelRun } from 'workflow/api';
32+
33+
export async function GET() {
34+
const runs = await listRuns({ status: 'running', pagination: { limit: 20 } });
35+
return Response.json({ runs: runs.data });
36+
}
37+
38+
export async function POST(request: Request) {
39+
const { runId } = await request.json();
40+
await cancelRun(runId);
41+
return Response.json({ ok: true });
42+
}
43+
```
44+
45+
This works the same way in development and production—just make sure the usual `WORKFLOW_*` environment variables are present so the correct world implementation can be chosen.
46+
47+
## Helper catalog
48+
49+
### Run helpers
50+
51+
| Function | Description |
52+
| --- | --- |
53+
| `createRun(data)` | Calls `world.runs.create` directly. Useful for advanced tooling/tests. |
54+
| `getWorkflowRun(id, params?)` | Fetches a workflow run record without wrapping it in the `Run` class. |
55+
| `updateRun(id, data)` | Partially updates a run record. |
56+
| `listRuns(params?)` | Lists runs with optional filters/pagination. |
57+
| `cancelRun(id, params?)` | Cancels a run. |
58+
| `pauseRun(id, params?)` / `resumeRun(id, params?)` | Pause/resume administrative helpers. |
59+
60+
### Step helpers
61+
62+
| Function | Description |
63+
| --- | --- |
64+
| `createStep(runId, data)` | Inserts a step record. |
65+
| `getStep(runId, stepId, params?)` | Retrieves a single step. |
66+
| `updateStep(runId, stepId, data)` | Updates status/attempt metadata. |
67+
| `listSteps(params)` | Lists steps for a run (with pagination + `resolveData`). |
68+
69+
### Events & hooks
70+
71+
| Function | Description |
72+
| --- | --- |
73+
| `createEvent(runId, data, params?)` | Writes workflow/step events. |
74+
| `listEvents(params)` | Lists events for a run. |
75+
| `listEventsByCorrelationId(params)` | Lists events for a correlation ID across runs. |
76+
| `createHook(runId, data, params?)` | Creates a hook. |
77+
| `getHook(id, params?)` | Fetches hook metadata. |
78+
| `listHooks(params)` | Lists hooks. |
79+
| `disposeHook(id, params?)` | Disposes a hook. |
80+
| `getHookByToken(token, params?)` | Continues to be exported for convenience. |
81+
82+
### Queue, streams, and lifecycle helpers
83+
84+
| Function | Description |
85+
| --- | --- |
86+
| `getDeploymentId()` | Returns the deployment ID that queue operations will use. |
87+
| `queue(name, payload, opts?)` | Enqueue workflow/step invocations manually. |
88+
| `createQueueHandler(prefix, handler)` | Builds queue HTTP handlers (the same API the runtime uses). |
89+
| `writeToStream(name, chunk)` / `closeStream(name)` / `readFromStream(name, startIndex?)` | Direct streaming helpers. |
90+
| `startWorld()` | Invokes `world.start?.()` if provided by your world implementation. |
91+
92+
## Testing tips
93+
94+
Use `setWorld` to stub custom worlds in tests so that the helpers continue to work without hitting real infrastructure:
95+
96+
```typescript
97+
import { setWorld } from 'workflow/runtime';
98+
import { listRuns } from 'workflow/api';
99+
100+
beforeEach(() => {
101+
setWorld({
102+
// Provide a minimal mock World
103+
runs: {
104+
list: async () => ({ data: [], cursor: null, hasMore: false }),
105+
create: async () => { throw new Error('not implemented'); },
106+
get: async () => { throw new Error('not implemented'); },
107+
update: async () => { throw new Error('not implemented'); },
108+
cancel: async () => { throw new Error('not implemented'); },
109+
pause: async () => { throw new Error('not implemented'); },
110+
resume: async () => { throw new Error('not implemented'); },
111+
},
112+
steps: { /* ... */ },
113+
events: { /* ... */ },
114+
hooks: { /* ... */ },
115+
getDeploymentId: async () => 'test',
116+
queue: async () => ({ messageId: 'msg_test' }),
117+
createQueueHandler: () => async () => new Response(),
118+
writeToStream: async () => {},
119+
closeStream: async () => {},
120+
readFromStream: async () => new ReadableStream(),
121+
} as any);
122+
});
123+
124+
afterEach(() => setWorld(undefined));
125+
126+
it('lists runs without contacting real services', async () => {
127+
const runs = await listRuns();
128+
expect(runs.data).toHaveLength(0);
129+
});
130+
```
131+
132+
This mirrors how the new automated tests stub the world singleton.

docs/content/docs/foundations/starting-workflows.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,27 @@ export async function GET(request: Request) {
210210
}
211211
```
212212

213+
### Programmatically list and control runs
214+
215+
If you want to show workflow progress directly in your product UI—or let trusted operators retry or cancel work—you can now call the same administrative helpers the CLI uses, straight from `workflow/api`.
216+
217+
```typescript lineNumbers
218+
import { listRuns, cancelRun } from 'workflow/api';
219+
220+
export async function GET() {
221+
const runs = await listRuns({ status: 'running', pagination: { limit: 20 } });
222+
return Response.json(runs.data);
223+
}
224+
225+
export async function POST(request: Request) {
226+
const { runId } = await request.json();
227+
await cancelRun(runId);
228+
return Response.json({ ok: true });
229+
}
230+
```
231+
232+
Need lower-level access? Call `getWorld()` (also exported from `workflow/api`) to reach the singleton directly, or `setWorld()` from `workflow/runtime` in your tests to install a mock world. See the [World helpers reference](/docs/api-reference/workflow-api/world) for every available function.
233+
213234
---
214235

215236
## Next Steps

packages/workflow/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@workflow/core": "workspace:*",
5353
"@workflow/errors": "workspace:*",
5454
"@workflow/typescript-plugin": "workspace:*",
55+
"@workflow/world": "workspace:*",
5556
"ms": "2.1.3",
5657
"@workflow/next": "workspace:*",
5758
"@workflow/nitro": "workspace:*",

0 commit comments

Comments
 (0)