openworkflow 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -345
- package/dist/backend-test/backend.testsuite.d.ts +20 -0
- package/dist/backend-test/backend.testsuite.d.ts.map +1 -0
- package/dist/{core → backend-test}/backend.testsuite.js +191 -59
- package/dist/backend-test/index.d.ts +2 -0
- package/dist/backend-test/index.d.ts.map +1 -0
- package/dist/backend-test/index.js +1 -0
- package/dist/{core/backend.d.ts → backend.d.ts} +7 -5
- package/dist/backend.d.ts.map +1 -0
- package/dist/{core/backend.js → backend.js} +0 -1
- package/dist/backend.testsuite.d.ts +20 -0
- package/dist/backend.testsuite.d.ts.map +1 -0
- package/dist/{core/backend-test-suite.js → backend.testsuite.js} +301 -171
- package/dist/bin/openworkflow.d.ts +3 -0
- package/dist/bin/openworkflow.d.ts.map +1 -0
- package/dist/bin/openworkflow.js +43 -0
- package/dist/chaos.test.d.ts +2 -0
- package/dist/chaos.test.d.ts.map +1 -0
- package/dist/chaos.test.js +88 -0
- package/dist/client.d.ts +141 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/{sdk/sdk.js → client.js} +43 -71
- package/dist/client.test.d.ts +2 -0
- package/dist/client.test.d.ts.map +1 -0
- package/dist/{sdk/sdk.test.js → client.test.js} +130 -14
- package/dist/core/duration.d.ts +4 -2
- package/dist/core/duration.d.ts.map +1 -1
- package/dist/core/duration.js +3 -2
- package/dist/core/duration.test.js +0 -1
- package/dist/core/error.d.ts +14 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +17 -0
- package/dist/core/error.test.d.ts +2 -0
- package/dist/core/error.test.d.ts.map +1 -0
- package/dist/core/error.test.js +60 -0
- package/dist/core/json.js +0 -1
- package/dist/core/result.d.ts +14 -4
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +10 -1
- package/dist/core/result.test.js +2 -2
- package/dist/core/retry.d.ts +0 -9
- package/dist/core/retry.d.ts.map +1 -1
- package/dist/core/retry.js +0 -15
- package/dist/core/schema.js +0 -1
- package/dist/core/step.d.ts +1 -32
- package/dist/core/step.d.ts.map +1 -1
- package/dist/core/step.js +0 -36
- package/dist/core/step.test.js +1 -75
- package/dist/core/workflow.d.ts +2 -47
- package/dist/core/workflow.d.ts.map +1 -1
- package/dist/core/workflow.js +0 -45
- package/dist/core/workflow.test.js +1 -104
- package/dist/driver.d.ts +116 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +1 -0
- package/dist/{execution/execution.d.ts → execution.d.ts} +4 -26
- package/dist/execution.d.ts.map +1 -0
- package/dist/{execution/execution.js → execution.js} +4 -5
- package/dist/execution.test.d.ts.map +1 -0
- package/dist/{execution/execution.test.js → execution.test.js} +4 -5
- package/dist/factory.d.ts +74 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +72 -0
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -0
- package/dist/node-sqlite/backend.d.ts +52 -0
- package/dist/node-sqlite/backend.d.ts.map +1 -0
- package/dist/node-sqlite/backend.js +673 -0
- package/dist/node-sqlite/index.d.ts +11 -0
- package/dist/node-sqlite/index.d.ts.map +1 -0
- package/dist/node-sqlite/index.js +7 -0
- package/dist/node-sqlite/sqlite.d.ts +60 -0
- package/dist/node-sqlite/sqlite.d.ts.map +1 -0
- package/dist/{backend-sqlite → node-sqlite}/sqlite.js +20 -3
- package/dist/postgres/backend.d.ts +44 -0
- package/dist/postgres/backend.d.ts.map +1 -0
- package/dist/postgres/backend.js +534 -0
- package/dist/postgres/backend.test.d.ts +2 -0
- package/dist/postgres/backend.test.d.ts.map +1 -0
- package/dist/postgres/backend.test.js +19 -0
- package/dist/postgres/driver.d.ts +81 -0
- package/dist/postgres/driver.d.ts.map +1 -0
- package/dist/postgres/driver.js +63 -0
- package/dist/postgres/index.d.ts +11 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +7 -0
- package/dist/postgres/internal.d.ts +2 -0
- package/dist/postgres/internal.d.ts.map +1 -0
- package/dist/postgres/internal.js +1 -0
- package/dist/postgres/postgres.d.ts +42 -0
- package/dist/postgres/postgres.d.ts.map +1 -0
- package/dist/postgres/postgres.js +233 -0
- package/dist/postgres/postgres.test.d.ts +2 -0
- package/dist/postgres/postgres.test.d.ts.map +1 -0
- package/dist/postgres/postgres.test.js +45 -0
- package/dist/postgres/scripts/db-migrate.d.ts +2 -0
- package/dist/postgres/scripts/db-migrate.d.ts.map +1 -0
- package/dist/postgres/scripts/db-migrate.js +4 -0
- package/dist/postgres/scripts/db-reset.d.ts +2 -0
- package/dist/postgres/scripts/db-reset.d.ts.map +1 -0
- package/dist/postgres/scripts/db-reset.js +5 -0
- package/dist/postgres/scripts/squawk.d.ts +2 -0
- package/dist/postgres/scripts/squawk.d.ts.map +1 -0
- package/dist/postgres/scripts/squawk.js +16 -0
- package/dist/postgres/vitest.global-setup.d.ts +3 -0
- package/dist/postgres/vitest.global-setup.d.ts.map +1 -0
- package/dist/postgres/vitest.global-setup.js +7 -0
- package/dist/postgres.d.ts +2 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +1 -0
- package/dist/registry.d.ts +27 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +48 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +109 -0
- package/dist/{backend-sqlite → sqlite}/backend.d.ts +8 -4
- package/dist/sqlite/backend.d.ts.map +1 -0
- package/dist/{backend-sqlite → sqlite}/backend.js +35 -9
- package/dist/sqlite/backend.test.d.ts +2 -0
- package/dist/sqlite/backend.test.d.ts.map +1 -0
- package/dist/sqlite/backend.test.js +50 -0
- package/dist/sqlite/driver.d.ts +79 -0
- package/dist/sqlite/driver.d.ts.map +1 -0
- package/dist/sqlite/driver.js +62 -0
- package/dist/sqlite/index.d.ts +13 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +11 -0
- package/dist/sqlite/internal.d.ts +2 -0
- package/dist/sqlite/internal.d.ts.map +1 -0
- package/dist/sqlite/internal.js +1 -0
- package/dist/{backend-sqlite → sqlite}/sqlite.d.ts +18 -2
- package/dist/sqlite/sqlite.d.ts.map +1 -0
- package/dist/sqlite/sqlite.js +246 -0
- package/dist/sqlite/sqlite.test.d.ts +2 -0
- package/dist/sqlite/sqlite.test.d.ts.map +1 -0
- package/dist/sqlite/sqlite.test.js +171 -0
- package/dist/sqlite.d.ts +2 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{worker/worker.d.ts → worker.d.ts} +11 -4
- package/dist/worker.d.ts.map +1 -0
- package/dist/{worker/worker.js → worker.js} +20 -11
- package/dist/{worker/worker.test.d.ts.map → worker.test.d.ts.map} +1 -1
- package/dist/{worker/worker.test.js → worker.test.js} +136 -22
- package/dist/workflow.d.ts +60 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +48 -0
- package/dist/workflow.test.d.ts +2 -0
- package/dist/workflow.test.d.ts.map +1 -0
- package/dist/workflow.test.js +84 -0
- package/package.json +28 -4
- package/dist/backend-sqlite/backend.d.ts.map +0 -1
- package/dist/backend-sqlite/backend.js.map +0 -1
- package/dist/backend-sqlite/index.d.ts +0 -2
- package/dist/backend-sqlite/index.d.ts.map +0 -1
- package/dist/backend-sqlite/index.js +0 -2
- package/dist/backend-sqlite/index.js.map +0 -1
- package/dist/backend-sqlite/sqlite.d.ts.map +0 -1
- package/dist/backend-sqlite/sqlite.js.map +0 -1
- package/dist/config/config.d.ts +0 -102
- package/dist/config/config.d.ts.map +0 -1
- package/dist/config/config.js +0 -29
- package/dist/config/config.js.map +0 -1
- package/dist/config/index.d.ts +0 -3
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -2
- package/dist/config/index.js.map +0 -1
- package/dist/config.d.ts +0 -28
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -41
- package/dist/config.js.map +0 -1
- package/dist/core/backend-test-suite.d.ts +0 -22
- package/dist/core/backend-test-suite.d.ts.map +0 -1
- package/dist/core/backend-test-suite.js.map +0 -1
- package/dist/core/backend.d.ts.map +0 -1
- package/dist/core/backend.js.map +0 -1
- package/dist/core/backend.testsuite.d.ts +0 -21
- package/dist/core/backend.testsuite.d.ts.map +0 -1
- package/dist/core/backend.testsuite.js.map +0 -1
- package/dist/core/duration.js.map +0 -1
- package/dist/core/duration.test.js.map +0 -1
- package/dist/core/json.js.map +0 -1
- package/dist/core/result.js.map +0 -1
- package/dist/core/result.test.js.map +0 -1
- package/dist/core/retry.js.map +0 -1
- package/dist/core/retry.test.d.ts +0 -2
- package/dist/core/retry.test.d.ts.map +0 -1
- package/dist/core/retry.test.js +0 -36
- package/dist/core/retry.test.js.map +0 -1
- package/dist/core/schema.js.map +0 -1
- package/dist/core/step.js.map +0 -1
- package/dist/core/step.test.js.map +0 -1
- package/dist/core/workflow.js.map +0 -1
- package/dist/core/workflow.test.js.map +0 -1
- package/dist/execution/execution.d.ts.map +0 -1
- package/dist/execution/execution.js.map +0 -1
- package/dist/execution/execution.test.d.ts.map +0 -1
- package/dist/execution/execution.test.js.map +0 -1
- package/dist/global.d.ts +0 -62
- package/dist/global.d.ts.map +0 -1
- package/dist/global.js +0 -78
- package/dist/global.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/sdk/sdk.d.ts +0 -182
- package/dist/sdk/sdk.d.ts.map +0 -1
- package/dist/sdk/sdk.js.map +0 -1
- package/dist/sdk/sdk.test.d.ts +0 -2
- package/dist/sdk/sdk.test.d.ts.map +0 -1
- package/dist/sdk/sdk.test.js.map +0 -1
- package/dist/worker/worker.d.ts.map +0 -1
- package/dist/worker/worker.js.map +0 -1
- package/dist/worker/worker.test.js.map +0 -1
- /package/dist/{execution/execution.test.d.ts → execution.test.d.ts} +0 -0
- /package/dist/{worker/worker.test.d.ts → worker.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -8,40 +8,10 @@ OpenWorkflow is a TypeScript framework for building durable, resumable workflows
|
|
|
8
8
|
that can pause for seconds or months, survive crashes and deploys, and resume
|
|
9
9
|
exactly where they left off - all without extra servers to manage.
|
|
10
10
|
|
|
11
|
-
> OpenWorkflow is in active development and moving quickly. Check out the
|
|
12
|
-
> [Roadmap](#roadmap) for what’s coming next.
|
|
13
|
-
|
|
14
|
-
## Quick Start
|
|
15
|
-
|
|
16
|
-
Prerequisites:
|
|
17
|
-
|
|
18
|
-
- Node.js
|
|
19
|
-
- PostgreSQL (and/or SQLite)
|
|
20
|
-
|
|
21
|
-
### 1. Install
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install openworkflow @openworkflow/backend-postgres @openworkflow/backend-sqlite
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
You only need to install the backend package(s) for the database you plan to
|
|
28
|
-
use.
|
|
29
|
-
|
|
30
|
-
### 2. Define a workflow
|
|
31
|
-
|
|
32
11
|
```ts
|
|
33
|
-
import {
|
|
34
|
-
import { BackendSqlite } from "@openworkflow/backend-sqlite";
|
|
35
|
-
import { OpenWorkflow } from "openworkflow";
|
|
12
|
+
import { defineWorkflow } from "openworkflow";
|
|
36
13
|
|
|
37
|
-
|
|
38
|
-
const backend = process.env["DATABASE_URL"]
|
|
39
|
-
? await BackendPostgres.connect(process.env["DATABASE_URL"])
|
|
40
|
-
: BackendSqlite.connect(); // optionally provide SQLite file path
|
|
41
|
-
|
|
42
|
-
const ow = new OpenWorkflow({ backend });
|
|
43
|
-
|
|
44
|
-
const sendWelcomeEmail = ow.defineWorkflow(
|
|
14
|
+
export const sendWelcomeEmail = defineWorkflow(
|
|
45
15
|
{ name: "send-welcome-email" },
|
|
46
16
|
async ({ input, step }) => {
|
|
47
17
|
const user = await step.run({ name: "fetch-user" }, async () => {
|
|
@@ -67,334 +37,62 @@ const sendWelcomeEmail = ow.defineWorkflow(
|
|
|
67
37
|
);
|
|
68
38
|
```
|
|
69
39
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Workers are background processes that execute your workflows. Start one in a
|
|
73
|
-
separate process or the same one as your app:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
const worker = ow.newWorker();
|
|
77
|
-
await worker.start();
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 4. Run workflows from your app
|
|
81
|
-
|
|
82
|
-
Trigger workflows from your web server, API, or any application code:
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
// In your API route handler
|
|
86
|
-
app.post("/users/:id/welcome", async (req, res) => {
|
|
87
|
-
// Run the workflow async and do not wait for the result
|
|
88
|
-
const runHandle = await sendWelcomeEmail.run({ userId: req.params.id });
|
|
89
|
-
res.json({ runId: runHandle.workflowRun.id });
|
|
90
|
-
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
That's it. Your workflow is now durable, resumable, and fault-tolerant.
|
|
94
|
-
|
|
95
|
-
## Core Concepts
|
|
96
|
-
|
|
97
|
-
### Workflows
|
|
98
|
-
|
|
99
|
-
Workflows are durable functions. They can contain multiple steps, make external
|
|
100
|
-
API calls, query databases, and perform complex logic. If a workflow is
|
|
101
|
-
interrupted (crash, deploy, server restart), it resumes from its last completed
|
|
102
|
-
step.
|
|
103
|
-
|
|
104
|
-
```ts
|
|
105
|
-
const workflow = ow.defineWorkflow(
|
|
106
|
-
{ name: "my-workflow" },
|
|
107
|
-
async ({ input, step }) => {
|
|
108
|
-
// Your workflow logic here
|
|
109
|
-
return result;
|
|
110
|
-
},
|
|
111
|
-
);
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Steps
|
|
115
|
-
|
|
116
|
-
Steps are the building blocks of workflows. Each step is executed exactly once
|
|
117
|
-
and its result is memoized. Steps let you break workflows into checkpoints.
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
const result = await step.run({ name: "step-name" }, async () => {
|
|
121
|
-
// This function runs once. If the workflow restarts,
|
|
122
|
-
// this returns the cached result instead of re-running.
|
|
123
|
-
return await someAsyncWork();
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
**Why steps matter**: Imagine a workflow that charges a credit card, then sends
|
|
128
|
-
an email. Without steps, if your server crashes after charging the card, the
|
|
129
|
-
workflow would retry from the beginning and charge the customer twice. With
|
|
130
|
-
steps, the charge is memoized. The retry skips it and goes straight to sending
|
|
131
|
-
the email.
|
|
132
|
-
|
|
133
|
-
### Workers
|
|
134
|
-
|
|
135
|
-
Workers are long-running processes that poll your database for pending workflows
|
|
136
|
-
and execute them. You can run multiple workers for high availability and scale.
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
const worker = ow.newWorker({ concurrency: 20 });
|
|
140
|
-
await worker.start();
|
|
141
|
-
|
|
142
|
-
// & to shut down...
|
|
143
|
-
await worker.stop(); // waits for in-flight workflows to complete
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Workers are stateless. They can be started, stopped, and deployed independently.
|
|
147
|
-
Your database is the source of truth.
|
|
148
|
-
|
|
149
|
-
### How it Works
|
|
150
|
-
|
|
151
|
-
1. **Your app starts a workflow**: A row is inserted into the `workflow_runs`
|
|
152
|
-
table with status `pending`.
|
|
153
|
-
2. **A worker picks it up**: The worker polls the database, claims the workflow,
|
|
154
|
-
and sets its status to `running`.
|
|
155
|
-
3. **The worker executes steps**: Each step is recorded in the `step_attempts`
|
|
156
|
-
table. If a step succeeds, its result is cached.
|
|
157
|
-
4. **The workflow completes**: The worker updates the `workflow_run` status to
|
|
158
|
-
`completed` or `failed`.
|
|
159
|
-
5. **If the worker crashes**: The workflow becomes visible to other workers via
|
|
160
|
-
a heartbeat timeout. Another worker picks it up, loads the cached step
|
|
161
|
-
results, and resumes from the next step.
|
|
162
|
-
|
|
163
|
-
## Advanced Patterns
|
|
164
|
-
|
|
165
|
-
### Parallel Steps
|
|
166
|
-
|
|
167
|
-
Run multiple steps concurrently using `Promise.all`:
|
|
168
|
-
|
|
169
|
-
```ts
|
|
170
|
-
const [user, subscription, settings] = await Promise.all([
|
|
171
|
-
step.run({ name: "fetch-user" }, async () => {
|
|
172
|
-
await db.users.findOne({ id: input.userId });
|
|
173
|
-
}),
|
|
174
|
-
step.run({ name: "fetch-subscription" }, async () => {
|
|
175
|
-
await stripe.subscriptions.retrieve(input.subId);
|
|
176
|
-
}),
|
|
177
|
-
step.run({ name: "fetch-settings" }, async () => {
|
|
178
|
-
await db.settings.findOne({ userId: input.userId });
|
|
179
|
-
}),
|
|
180
|
-
]);
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Each step is still memoized individually. If the workflow crashes mid-execution,
|
|
184
|
-
completed steps return instantly on resume.
|
|
185
|
-
|
|
186
|
-
### Automatic Retries
|
|
187
|
-
|
|
188
|
-
Steps can retry automatically with exponential backoff:
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
const data = await step.run({ name: "fetch-external-api" }, async () => {
|
|
192
|
-
// If this throws, the step retries automatically
|
|
193
|
-
return await externalAPI.getData();
|
|
194
|
-
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Configure retry behavior at the workflow or step level (coming soon) or handle
|
|
198
|
-
errors explicitly in your step functions.
|
|
199
|
-
|
|
200
|
-
### Sleeping (Pausing) Workflows
|
|
201
|
-
|
|
202
|
-
You can pause a workflow until a future time and, because sleeping releases the
|
|
203
|
-
worker slot, you can pause thousands of workflows without tying up compute:
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
// Pause for 1 hour (durable, non-blocking)
|
|
207
|
-
await step.sleep("wait-one-hour", "1h");
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The sleep step is memoized after it completes. If the workflow is replayed again
|
|
211
|
-
(e.g. due to a later retry) the completed sleep is not re-applied.
|
|
212
|
-
|
|
213
|
-
#### Duration Formats
|
|
214
|
-
|
|
215
|
-
Durations accept a number followed by a unit:
|
|
216
|
-
|
|
217
|
-
| Unit | Aliases | Examples |
|
|
218
|
-
| ------------ | --------------------- | ---------------- |
|
|
219
|
-
| milliseconds | `ms`, `msec`, `msecs` | `100ms`, `1.5ms` |
|
|
220
|
-
| seconds | `s`, `sec`, `secs` | `5s`, `0.25s` |
|
|
221
|
-
| minutes | `m`, `min`, `mins` | `2m`, `1.5m` |
|
|
222
|
-
| hours | `h`, `hr`, `hrs` | `1h`, `0.25h` |
|
|
223
|
-
| days | `d`, `day(s)` | `1d`, `0.5d` |
|
|
224
|
-
| weeks | `w`, `week(s)` | `1w`, `2w` |
|
|
225
|
-
| months | `mo`, `month(s)` | `1mo`, `2mo` |
|
|
226
|
-
| years | `y`, `yr`, `yrs` | `1y`, `2yr` |
|
|
227
|
-
|
|
228
|
-
See more examples of accepted duration formats and aliases in the
|
|
229
|
-
[tests](https://github.com/openworkflowdev/openworkflow/blob/main/packages/openworkflow/core/duration.test.ts).
|
|
230
|
-
|
|
231
|
-
### Type Safety
|
|
232
|
-
|
|
233
|
-
Workflows are fully typed. Define input and output types for compile-time
|
|
234
|
-
safety:
|
|
235
|
-
|
|
236
|
-
```ts
|
|
237
|
-
interface ProcessOrderInput {
|
|
238
|
-
orderId: string;
|
|
239
|
-
userId: string;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
interface ProcessOrderOutput {
|
|
243
|
-
paymentId: string;
|
|
244
|
-
shipmentId: string;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const processOrder = ow.defineWorkflow<ProcessOrderInput, ProcessOrderOutput>(
|
|
248
|
-
{ name: "process-order" },
|
|
249
|
-
async ({ input, step }) => {
|
|
250
|
-
// input is typed as ProcessOrderInput
|
|
251
|
-
// return type must match ProcessOrderOutput
|
|
252
|
-
return { paymentId: "...", shipmentId: "..." };
|
|
253
|
-
},
|
|
254
|
-
);
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Waiting for Results
|
|
258
|
-
|
|
259
|
-
You can wait for a workflow to complete and get its result:
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
const run = await myWorkflow.run({ data: "..." });
|
|
263
|
-
|
|
264
|
-
// Wait for the workflow to finish (polls the database)
|
|
265
|
-
const result = await run.result();
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Canceling Workflows
|
|
269
|
-
|
|
270
|
-
You can cancel a workflow that is pending, running, or sleeping to prevent a
|
|
271
|
-
workflow from continuing on to the next step:
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
const handle = await myWorkflow.run({ data: "..." });
|
|
275
|
-
|
|
276
|
-
// Cancel the workflow
|
|
277
|
-
await handle.cancel();
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### Workflow Versioning
|
|
40
|
+
## Quick Start
|
|
281
41
|
|
|
282
|
-
|
|
283
|
-
compatibility.
|
|
42
|
+
**Prerequisites:** Node.js & PostgreSQL (or SQLite)
|
|
284
43
|
|
|
285
|
-
|
|
44
|
+
### Install
|
|
286
45
|
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
{ name: "my-workflow", version: "v2" },
|
|
290
|
-
async ({ input, step, version }) => {
|
|
291
|
-
if (version === "v2") {
|
|
292
|
-
// v2 runs go here
|
|
293
|
-
await step.run({ name: "new-step" }, async () => {
|
|
294
|
-
// legacy logic
|
|
295
|
-
});
|
|
296
|
-
} else {
|
|
297
|
-
// v1 runs go here
|
|
298
|
-
await step.run({ name: "old-step" }, async () => {
|
|
299
|
-
// ...
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
);
|
|
46
|
+
```bash
|
|
47
|
+
npx @openworkflow/cli init
|
|
304
48
|
```
|
|
305
49
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
You can require `.run()` callers to provide specific inputs by supplying a
|
|
309
|
-
`schema` when defining the workflow. The schema is evaluated before the run is
|
|
310
|
-
enqueued, so invalid requests fail immediately.
|
|
311
|
-
|
|
312
|
-
```ts
|
|
313
|
-
import { z } from "zod";
|
|
314
|
-
|
|
315
|
-
const summarizeDoc = ow.defineWorkflow(
|
|
316
|
-
{
|
|
317
|
-
name: "summarize",
|
|
318
|
-
schema: z.object({
|
|
319
|
-
docUrl: z.string().url(),
|
|
320
|
-
}),
|
|
321
|
-
},
|
|
322
|
-
async ({ input, step }) => {
|
|
323
|
-
// `input` has type { docUrl: string }
|
|
324
|
-
},
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
// Throws before enqueueing the workflow because the input isn't a URL
|
|
328
|
-
await summarizeDoc.run({ docUrl: "not-a-url" });
|
|
329
|
-
```
|
|
50
|
+
The CLI will guide you through setup and generate everything you need to get
|
|
51
|
+
started.
|
|
330
52
|
|
|
331
|
-
|
|
332
|
-
custom logic or lightweight checks). Libraries such as Zod, ArkType, Valibot,
|
|
333
|
-
and Yup.
|
|
53
|
+
For more details, check out our [docs](https://openworkflow.dev/docs).
|
|
334
54
|
|
|
335
|
-
##
|
|
55
|
+
## Features
|
|
336
56
|
|
|
337
|
-
- **
|
|
338
|
-
- **
|
|
339
|
-
- **
|
|
340
|
-
|
|
341
|
-
- **
|
|
342
|
-
- **
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
});
|
|
348
|
-
```
|
|
349
|
-
- **Namespaces** (optional): Use `namespaceId` in your backend configuration to
|
|
350
|
-
isolate workflows per environment:
|
|
351
|
-
```ts
|
|
352
|
-
const backend = await BackendPostgres.connect(postgresUrl, {
|
|
353
|
-
namespaceId: "production",
|
|
354
|
-
});
|
|
355
|
-
```
|
|
57
|
+
- ✅ **Durable** - Workflows survive crashes and deploys
|
|
58
|
+
- ✅ **Resumable** - Pick up exactly where you left off
|
|
59
|
+
- ✅ **Type-safe** - Full TypeScript support
|
|
60
|
+
- ✅ **Step memoization** - Never repeat completed work
|
|
61
|
+
- ✅ **Automatic retries** - Built-in exponential backoff
|
|
62
|
+
- ✅ **Long pauses** - Sleep for seconds or months
|
|
63
|
+
- ✅ **Parallel execution** - Run steps concurrently
|
|
64
|
+
- ✅ **No extra servers** - Uses your existing database
|
|
65
|
+
- ✅ **Dashboard included** - Monitor and debug workflows
|
|
66
|
+
- ✅ **Production ready** - PostgreSQL and SQLite support
|
|
356
67
|
|
|
357
|
-
##
|
|
68
|
+
## Documentation
|
|
358
69
|
|
|
359
|
-
-
|
|
360
|
-
|
|
361
|
-
-
|
|
362
|
-
-
|
|
363
|
-
|
|
70
|
+
- [Documentation](https://openworkflow.dev/docs)
|
|
71
|
+
- [Quick Start Guide](https://openworkflow.dev/docs/quickstart)
|
|
72
|
+
- [Core Concepts](https://openworkflow.dev/docs/core-concepts)
|
|
73
|
+
- [Advanced Patterns](https://openworkflow.dev/docs/advanced-patterns)
|
|
74
|
+
- [Production Checklist](https://openworkflow.dev/docs/production)
|
|
364
75
|
|
|
365
|
-
##
|
|
76
|
+
## Architecture
|
|
366
77
|
|
|
367
|
-
|
|
78
|
+
Read
|
|
79
|
+
[ARCHITECTURE.md](https://github.com/openworkflowdev/openworkflow/blob/main/ARCHITECTURE.md)
|
|
80
|
+
for a deep dive into how OpenWorkflow works under the hood.
|
|
368
81
|
|
|
369
|
-
|
|
370
|
-
- ✅ Worker with concurrency control
|
|
371
|
-
- ✅ Step memoization & retries
|
|
372
|
-
- ✅ Graceful shutdown
|
|
373
|
-
- ✅ Parallel step execution
|
|
374
|
-
- ✅ Sleeping (pausing) workflows
|
|
375
|
-
- ✅ Workflow versioning
|
|
376
|
-
- ✅ Workflow cancelation
|
|
82
|
+
## Examples
|
|
377
83
|
|
|
378
|
-
|
|
84
|
+
Check out
|
|
85
|
+
[examples/](https://github.com/openworkflowdev/openworkflow/tree/main/examples)
|
|
86
|
+
for working examples.
|
|
379
87
|
|
|
380
|
-
|
|
381
|
-
> inspect workflow and step state directly in PostgreSQL or SQLite
|
|
382
|
-
> (workflow_runs and step_runs tables). A CLI and dashboard are planned for an
|
|
383
|
-
> upcoming release to make debugging and monitoring much easier.
|
|
88
|
+
## Contributing
|
|
384
89
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
- Idempotency keys
|
|
389
|
-
- Rollback / compensation functions
|
|
390
|
-
- Configurable retry policies
|
|
391
|
-
- Signals for external events
|
|
392
|
-
- Native OpenTelemetry integration
|
|
393
|
-
- Additional backends (Redis)
|
|
394
|
-
- Additional languages (Go, Python)
|
|
90
|
+
We welcome contributions! Please read
|
|
91
|
+
[CONTRIBUTING.md](https://github.com/openworkflowdev/openworkflow/blob/main/CONTRIBUTING.md)
|
|
92
|
+
before submitting a pull request.
|
|
395
93
|
|
|
396
|
-
##
|
|
94
|
+
## Community
|
|
397
95
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
https://
|
|
96
|
+
- [GitHub Issues](https://github.com/openworkflowdev/openworkflow/issues) -
|
|
97
|
+
Report bugs and request features
|
|
98
|
+
- [Roadmap](https://openworkflow.dev/docs/roadmap) - See what's coming next
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Backend } from "../backend.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for the Backend test suite.
|
|
4
|
+
*/
|
|
5
|
+
export interface TestBackendOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new isolated Backend instance.
|
|
8
|
+
*/
|
|
9
|
+
setup: () => Promise<Backend>;
|
|
10
|
+
/**
|
|
11
|
+
* Cleans up a Backend instance.
|
|
12
|
+
*/
|
|
13
|
+
teardown: (backend: Backend) => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runs the Backend test suite.
|
|
17
|
+
* @param options - Test suite options
|
|
18
|
+
*/
|
|
19
|
+
export declare function testBackend(options: TestBackendOptions): void;
|
|
20
|
+
//# sourceMappingURL=backend.testsuite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.testsuite.d.ts","sourceRoot":"","sources":["../../backend-test/backend.testsuite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAM7C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B;;OAEG;IACH,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CA8sC7D"}
|