Skip to content

Commit 4b5d6dd

Browse files
committed
fix workflow runtime release surface
1 parent 0de8e83 commit 4b5d6dd

92 files changed

Lines changed: 4524 additions & 1652 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## ✅ Checklist
66

7-
- [ ] I have followed the steps in the [Contributing guide](https://github.com/TanStack/template/blob/main/CONTRIBUTING.md).
7+
- [ ] I have followed the steps in the [Contributing guide](https://github.com/TanStack/workflow/blob/main/CONTRIBUTING.md).
88
- [ ] I have tested this code locally with `pnpm run test:pr`.
99

1010
## 🚀 Release Impact

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ id: contributing
77

88
## Questions
99

10-
If you have questions about implementation details, help or support, then please use our dedicated community forum at [Github Discussions](https://github.com/tanstack/template/discussions) **PLEASE NOTE:** If you choose to instead open an issue for your question, your issue will be immediately closed and redirected to the forum.
10+
If you have questions about implementation details, help, or support, use [GitHub Discussions](https://github.com/tanstack/workflow/discussions). Issues should be reserved for reproducible bugs and concrete feature requests.
1111

1212
## Reporting Issues
1313

14-
If you have found what you think is a bug, please [file an issue](https://github.com/tanstack/template/issues/new). **PLEASE NOTE:** Issues that are identified as implementation questions or non-issues will be immediately closed and redirected to [Github Discussions](https://github.com/tanstack/template/discussions)
14+
If you have found what you think is a bug, please [file an issue](https://github.com/tanstack/workflow/issues/new) with a minimal reproduction and the expected behavior.
1515

1616
## Suggesting new features
1717

@@ -27,7 +27,7 @@ Before proceeding with development, ensure you match one of the following criter
2727

2828
## Development Workflow
2929

30-
- Fork this repository, we prefer the `feat-*` branch name style
30+
- Fork this repository; we prefer the `feat-*` branch name style
3131
- Ensure you have `pnpm` installed
3232
- Install projects dependencies and linkages by running `pnpm install`
3333
- Auto-build and auto-test files as you edit by running `pnpm dev`

README.md

Lines changed: 70 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,87 @@
1-
<div align="center">
2-
<img src="./media/header_template.png" alt="TanStack Template" />
3-
</div>
1+
# TanStack Workflow
42

5-
<br />
3+
Type-safe durable execution for TypeScript. Workflows are ordinary async functions that can pause, persist progress to an append-only log, and resume after approvals, webhooks, timers, or process restarts.
64

7-
<div align="center">
8-
<a href="https://www.npmjs.com/package/@tanstack/template" target="\_parent">
9-
<img alt="npm downloads" src="https://img.shields.io/npm/dm/@tanstack/template.svg" />
10-
</a>
11-
<a href="https://github.com/TanStack/template" target="\_parent">
12-
<img alt="GitHub stars" src="https://img.shields.io/github/stars/TanStack/template.svg?style=social&label=Star" />
13-
</a>
14-
<a href="https://bundlephobia.com/result?p=@tanstack/react-template@latest" target="\_parent">
15-
<img alt="Bundle size" src="https://badgen.net/bundlephobia/minzip/@tanstack/react-template@latest" />
16-
</a>
17-
</div>
18-
19-
<div align="center">
20-
<a href="#badge">
21-
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
22-
</a>
23-
<a href="#badge">
24-
<img src="https://img.shields.io/github/v/release/tanstack/template" alt="Release"/>
25-
</a>
26-
<a href="https://twitter.com/tan_stack">
27-
<img src="https://img.shields.io/twitter/follow/tan_stack.svg?style=social" alt="Follow @TanStack"/>
28-
</a>
29-
</div>
30-
31-
<div align="center">
32-
33-
### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
34-
35-
</div>
36-
37-
# TanStack Template
38-
39-
> [!NOTE]
40-
> TanStack Template is a starting point for new TanStack libraries. Replace this README content with product-specific copy before release.
41-
42-
Build a TanStack library with a framework-agnostic core, React and Solid adapters, devtools packages, documentation, examples, CI, release automation, and package quality checks already wired together.
5+
```bash
6+
pnpm add @tanstack/workflow-core
7+
```
438

44-
- Framework-agnostic core package ready for domain logic
45-
- React and Solid adapters with matching examples and docs structure
46-
- Devtools package placeholders for framework-specific integrations
47-
- pnpm, Nx, TypeScript, tsdown, Vitest, ESLint, Prettier, Changesets, and TypeDoc setup
48-
- GitHub workflows for PR checks, release, preview publishing, provenance, and autofix
9+
Install `zod` or another Standard Schema-compatible library if you want runtime validation for workflow inputs, outputs, state, or signal payloads.
10+
11+
## Example
12+
13+
```ts
14+
import {
15+
createWorkflow,
16+
inMemoryRunStore,
17+
runWorkflow,
18+
} from '@tanstack/workflow-core'
19+
import { z } from 'zod'
20+
21+
const checkout = createWorkflow({
22+
id: 'checkout',
23+
input: z.object({ userId: z.string(), amount: z.number() }),
24+
output: z.object({ status: z.enum(['approved', 'rejected']) }),
25+
}).handler(async (ctx) => {
26+
const charge = await ctx.step('charge-card', (stepCtx) =>
27+
stripe.charges.create(
28+
{ customer: ctx.input.userId, amount: ctx.input.amount },
29+
{ idempotencyKey: stepCtx.id },
30+
),
31+
)
32+
33+
if (ctx.input.amount > 10_000) {
34+
const decision = await ctx.approve({ title: 'Approve large charge?' })
35+
if (!decision.approved) return { status: 'rejected' as const }
36+
}
37+
38+
await ctx.step('send-receipt', () => sendReceipt(charge.id))
39+
return { status: 'approved' as const }
40+
})
41+
42+
const store = inMemoryRunStore()
43+
44+
for await (const event of runWorkflow({
45+
workflow: checkout,
46+
input: { userId: 'cus_123', amount: 4200 },
47+
runStore: store,
48+
})) {
49+
console.log(event.type, event)
50+
}
51+
```
4952

50-
### <a href="https://tanstack.com/template">Read the docs -></a>
53+
## Core Ideas
5154

52-
<br />
55+
- Side effects live inside `ctx.step(id, fn)`, which records results and skips re-execution on replay.
56+
- `ctx.waitForEvent`, `ctx.approve`, `ctx.sleep`, and `ctx.sleepUntil` pause runs until the host delivers a matching signal or approval.
57+
- `ctx.now()` and `ctx.uuid()` record deterministic values for replay.
58+
- Middleware can extend `ctx` with typed dependencies such as users, database handles, or tracing.
59+
- Storage is pluggable through `RunStore`; the package ships an in-memory store for local development and tests.
5360

54-
> [!NOTE]
55-
> New libraries created from this template currently include these adapter packages:
56-
>
57-
> - [**React Template**](https://tanstack.com/template/latest/docs/framework/react/adapter)
58-
> - [**Solid Template**](https://tanstack.com/template/latest/docs/framework/solid/adapter)
61+
## Packages
5962

60-
## Template Usage
63+
- `@tanstack/workflow-core`: engine, workflow builder, middleware, event types, request parsing helpers, version routing, and in-memory `RunStore`.
6164

62-
Start with [TEMPLATE_GUIDE.md](./TEMPLATE_GUIDE.md), then replace the placeholder names, package descriptions, docs, examples, source code, issue templates, and README copy with the new library's product-specific content.
65+
Storage adapters, framework bindings, and devtools are planned as follow-up packages.
6366

64-
## Development Commands
67+
## Development
6568

6669
```bash
6770
pnpm install
68-
pnpm build:all
69-
pnpm test:lib
70-
pnpm test:pr
71-
pnpm lint:all
72-
pnpm format
73-
pnpm generate-docs
74-
pnpm watch
71+
pnpm --filter @tanstack/workflow-core test:lib
72+
pnpm --filter @tanstack/workflow-core test:types
73+
pnpm --filter @tanstack/workflow-core build
74+
pnpm test
7575
```
7676

77-
## Get Involved
78-
79-
- We welcome issues and pull requests.
80-
- Participate in [GitHub discussions](https://github.com/TanStack/template/discussions).
81-
- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ).
82-
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions.
83-
84-
## Partners
85-
86-
<div align="center">
87-
88-
<table align="center">
89-
<tr>
90-
<td>
91-
<a href="https://www.coderabbit.ai/?via=tanstack">
92-
<picture>
93-
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-D643Zkrv.svg" height="40" />
94-
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/coderabbit-light-CIzGLYU_.svg" height="40" />
95-
<img src="https://tanstack.com/assets/coderabbit-light-DVMJ2jHi.svg" height="40" alt="CodeRabbit" />
96-
</picture>
97-
</a>
98-
</td>
99-
<td>
100-
<a href="https://www.cloudflare.com?utm_source=tanstack">
101-
<picture>
102-
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-Co-Tyjbl.svg" height="60" />
103-
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/cloudflare-black-6Ojsn8yh.svg" height="60" />
104-
<img src="https://tanstack.com/assets/cloudflare-black-CPufaW0B.svg" height="60" alt="Cloudflare" />
105-
</picture>
106-
</a>
107-
</td>
108-
</tr>
109-
</table>
110-
111-
</div>
112-
113-
## Explore the TanStack Ecosystem
114-
115-
- <a href="https://github.com/tanstack/config"><b>TanStack Config</b></a> - Tooling for JS/TS packages
116-
- <a href="https://github.com/tanstack/db"><b>TanStack DB</b></a> - Reactive sync client store
117-
- <a href="https://github.com/tanstack/devtools"><b>TanStack DevTools</b></a> - Unified devtools panel
118-
- <a href="https://github.com/tanstack/form"><b>TanStack Form</b></a> - Type-safe form state
119-
- <a href="https://github.com/tanstack/hotkeys"><b>TanStack Hotkeys</b></a> - Type-safe keyboard shortcuts
120-
- <a href="https://github.com/tanstack/query"><b>TanStack Query</b></a> - Async state and caching
121-
- <a href="https://github.com/tanstack/router"><b>TanStack Router</b></a> - Type-safe routing, caching and URL state
122-
- <a href="https://github.com/tanstack/start"><b>TanStack Start</b></a> - Full-stack SSR and streaming
123-
- <a href="https://github.com/tanstack/store"><b>TanStack Store</b></a> - Reactive data store
124-
- <a href="https://github.com/tanstack/table"><b>TanStack Table</b></a> - Headless datagrids
125-
- <a href="https://github.com/tanstack/virtual"><b>TanStack Virtual</b></a> - Virtualized rendering
126-
127-
... and more at <a href="https://tanstack.com"><b>TanStack.com &raquo;</b></a>
77+
## Docs
78+
79+
- [Overview](./docs/overview.md)
80+
- [Installation](./docs/installation.md)
81+
- [Quick start](./docs/quick-start.md)
82+
- [Primitives](./docs/concepts/primitives.md)
83+
- [Replay and resume](./docs/concepts/replay-and-resume.md)
84+
- [Scheduling](./docs/concepts/scheduling.md)
12885

12986
## License
13087

docs/concepts/primitives.md

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ await ctx.sleepUntil(nextMidnight()) // wake at a wall-clock time
4545
Pause until the host delivers a signal with this `name`. Returns the payload.
4646

4747
```ts
48+
const now = await ctx.now()
4849
const payload = await ctx.waitForEvent('webhook-received', {
4950
schema: z.object({ reference: z.string() }),
50-
meta: { source: 'stripe' }, // visible to the host driver
51-
deadline: Date.now() + 86_400_000, // host wakes if not delivered
51+
meta: { source: 'stripe' }, // visible to the host driver
52+
deadline: now + 86_400_000, // host wakes if not delivered
5253
})
5354
```
5455

@@ -103,23 +104,6 @@ await ctx.step('long-fetch', (stepCtx) =>
103104
)
104105
```
105106

106-
## `retry(fn, opts)`
107-
108-
Library-level helper for retrying a **composite** of multiple yields. Prefer `ctx.step({ retry })` for single steps.
109-
110-
```ts
111-
import { retry } from '@tanstack/workflow-core'
112-
113-
await retry(
114-
async () => {
115-
const a = await ctx.step('a', fetchA)
116-
const b = await ctx.step('b', () => fetchB(a))
117-
return { a, b }
118-
},
119-
{ attempts: 3, backoff: 'exponential' },
120-
)
121-
```
122-
123107
## `succeed` / `fail`
124108

125109
Tagged return helpers. Avoids `as const` clutter on discriminated unions.

docs/concepts/replay-and-resume.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ Append-only. Optimistic-CAS on `expectedNextIndex`. Stored via `RunStore.appendE
1212
- `NOW_RECORDED` / `UUID_RECORDED`
1313
- `RUN_FINISHED` / `RUN_ERRORED`
1414

15+
**Coordination events** — persisted so hosts and resume calls can identify the pending wait:
16+
- `SIGNAL_AWAITED`
17+
- `APPROVAL_REQUESTED`
18+
1519
**Observability-only events** — emit-only, not persisted:
1620
- `RUN_STARTED`, `STEP_STARTED`
1721
- `STATE_DELTA`
@@ -120,7 +124,7 @@ Same engine. One invocation drives the run to its next pause or completion. The
120124

121125
## Cleanup
122126

123-
`RunStore.deleteRun(runId, reason)` fires automatically on `finished` / `errored` / `aborted`. Paused runs persist until the host cleans them up or a TTL expires (in-memory store: 1h default).
127+
Terminal runs remain in the store so attach calls and webhook retries can read the final log. Stores decide their retention policy; the in-memory store expires non-paused runs after its TTL (1h default). Hosts can still call `RunStore.deleteRun(runId, reason)` when they want immediate cleanup.
124128

125129
## What the log contains, end to end
126130

docs/concepts/scheduling.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ async function tickAllSchedules(schedules: Array<Schedule>, runStore: RunStore)
215215
setInterval(() => tickAllSchedules(schedules, runStore), 30_000)
216216
```
217217

218-
A more durable version persists `nextFireAt` alongside each schedule definition; a deeper one elects a single leader to avoid duplicate ticks across instances. That whole layer is what a future `@tanstack/workflow-cron` package would provide — see [research/SCHEDULING.md](../../research/SCHEDULING.md) for the design sketch.
218+
A more durable version persists `nextFireAt` alongside each schedule definition; a deeper one elects a single leader to avoid duplicate ticks across instances. That whole layer is what a future `@tanstack/workflow-cron` package would provide. The design sketch lives in [`research/SCHEDULING.md`](https://github.com/TanStack/workflow/blob/main/research/SCHEDULING.md).
219219

220220
## Test pattern
221221

docs/config.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@
4343
"to": "concepts/scheduling"
4444
}
4545
]
46+
},
47+
{
48+
"label": "API Reference",
49+
"children": [
50+
{
51+
"label": "workflow-core",
52+
"to": "reference/index"
53+
}
54+
]
4655
}
4756
]
4857
}

docs/installation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Installation
22

33
```bash
4-
pnpm add @tanstack/workflow-core zod
4+
pnpm add @tanstack/workflow-core
55
```
66

7-
`zod` is a peer requirement only if you use `input` / `output` / `state` / `waitForEvent({ schema })` validation. Any [Standard Schema](https://github.com/standard-schema/standard-schema) library works.
7+
Install `zod` or another [Standard Schema](https://github.com/standard-schema/standard-schema) library if you use `input` / `output` / `state` / `waitForEvent({ schema })` validation.
88

99
## Storage
1010

docs/quick-start.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Quick start
22

3-
Copy-paste recipes. Each block runs as-is against `@tanstack/workflow-core` + `zod`.
3+
Minimal recipes for `@tanstack/workflow-core` + `zod`. Snippets use small local helpers like `collect(...)` and `findRunId(...)` when they need to drain an event stream.
44

55
## Install
66

@@ -56,13 +56,14 @@ const order = createWorkflow({
5656
const store = inMemoryRunStore()
5757
const start = await collect(runWorkflow({ workflow: order, input: { amount: 1500 }, runStore: store }))
5858
const runId = findRunId(start)
59+
const approvalId = start.find((e) => e.type === 'APPROVAL_REQUESTED')!.approvalId
5960

6061
// Resume — same workflow, same runStore, new approval delivery
6162
await collect(runWorkflow({
6263
workflow: order,
6364
runId,
6465
runStore: store,
65-
approval: { approvalId: 'a-1', approved: true },
66+
approval: { approvalId, approved: true },
6667
}))
6768
```
6869

@@ -126,11 +127,13 @@ const v2 = createWorkflow({ id: 'pipeline', version: 'v2' })
126127
.handler(async (ctx) => { /* v2 body */ })
127128

128129
// Engine reads workflowVersion from RunState and routes to the matching code.
130+
// startEvents are the events from the original v1 run.
131+
const approvalId = startEvents.find((e) => e.type === 'APPROVAL_REQUESTED')!.approvalId
129132
await collect(runWorkflow({
130133
workflow: v2, // current version
131134
runId, // started under v1
132135
runStore: store,
133-
approval: { approvalId: 'a-1', approved: true },
136+
approval: { approvalId, approved: true },
134137
}))
135138
```
136139

0 commit comments

Comments
 (0)