openworkflow 0.5.0 → 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.
Files changed (193) hide show
  1. package/README.md +40 -347
  2. package/dist/{testing → backend-test}/backend.testsuite.d.ts +1 -1
  3. package/dist/backend-test/backend.testsuite.d.ts.map +1 -0
  4. package/dist/{testing → backend-test}/backend.testsuite.js +8 -9
  5. package/dist/backend-test/index.d.ts +2 -0
  6. package/dist/backend-test/index.d.ts.map +1 -0
  7. package/dist/{testing → backend-test}/index.js +0 -1
  8. package/dist/backend.js +0 -1
  9. package/dist/backend.testsuite.d.ts +20 -0
  10. package/dist/backend.testsuite.d.ts.map +1 -0
  11. package/dist/backend.testsuite.js +1090 -0
  12. package/dist/bin/openworkflow.js +0 -1
  13. package/dist/chaos.test.d.ts +2 -0
  14. package/dist/chaos.test.d.ts.map +1 -0
  15. package/dist/chaos.test.js +88 -0
  16. package/dist/client.js +0 -1
  17. package/dist/client.test.d.ts +2 -0
  18. package/dist/client.test.d.ts.map +1 -0
  19. package/dist/client.test.js +311 -0
  20. package/dist/core/duration.js +0 -1
  21. package/dist/core/duration.test.d.ts +2 -0
  22. package/dist/core/duration.test.d.ts.map +1 -0
  23. package/dist/core/duration.test.js +263 -0
  24. package/dist/core/error.js +0 -1
  25. package/dist/core/error.test.d.ts +2 -0
  26. package/dist/core/error.test.d.ts.map +1 -0
  27. package/dist/core/error.test.js +60 -0
  28. package/dist/core/json.js +0 -1
  29. package/dist/core/result.js +0 -1
  30. package/dist/core/result.test.d.ts +2 -0
  31. package/dist/core/result.test.d.ts.map +1 -0
  32. package/dist/core/result.test.js +11 -0
  33. package/dist/core/retry.js +0 -1
  34. package/dist/core/schema.js +0 -1
  35. package/dist/core/step.js +0 -1
  36. package/dist/core/step.test.d.ts +2 -0
  37. package/dist/core/step.test.d.ts.map +1 -0
  38. package/dist/core/step.test.js +266 -0
  39. package/dist/core/workflow.js +0 -1
  40. package/dist/core/workflow.test.d.ts +2 -0
  41. package/dist/core/workflow.test.d.ts.map +1 -0
  42. package/dist/core/workflow.test.js +113 -0
  43. package/dist/driver.d.ts +116 -0
  44. package/dist/driver.d.ts.map +1 -0
  45. package/dist/driver.js +1 -0
  46. package/dist/execution.js +0 -1
  47. package/dist/execution.test.d.ts +2 -0
  48. package/dist/execution.test.d.ts.map +1 -0
  49. package/dist/execution.test.js +381 -0
  50. package/dist/factory.d.ts +74 -0
  51. package/dist/factory.d.ts.map +1 -0
  52. package/dist/factory.js +72 -0
  53. package/dist/index.js +0 -1
  54. package/dist/internal.d.ts +4 -5
  55. package/dist/internal.d.ts.map +1 -1
  56. package/dist/internal.js +1 -4
  57. package/dist/{backend-sqlite/index.d.ts → node-sqlite/backend.d.ts} +14 -4
  58. package/dist/node-sqlite/backend.d.ts.map +1 -0
  59. package/dist/{backend-sqlite/index.js → node-sqlite/backend.js} +23 -5
  60. package/dist/node-sqlite/index.d.ts +11 -0
  61. package/dist/node-sqlite/index.d.ts.map +1 -0
  62. package/dist/node-sqlite/index.js +7 -0
  63. package/dist/{backend-sqlite → node-sqlite}/sqlite.d.ts +2 -3
  64. package/dist/node-sqlite/sqlite.d.ts.map +1 -0
  65. package/dist/{backend-sqlite → node-sqlite}/sqlite.js +0 -1
  66. package/dist/{pg → postgres}/backend.d.ts +3 -1
  67. package/dist/postgres/backend.d.ts.map +1 -0
  68. package/dist/{pg → postgres}/backend.js +5 -5
  69. package/dist/postgres/backend.test.d.ts +2 -0
  70. package/dist/postgres/backend.test.d.ts.map +1 -0
  71. package/dist/postgres/backend.test.js +19 -0
  72. package/dist/postgres/driver.d.ts +81 -0
  73. package/dist/postgres/driver.d.ts.map +1 -0
  74. package/dist/postgres/driver.js +63 -0
  75. package/dist/postgres/index.d.ts +11 -0
  76. package/dist/postgres/index.d.ts.map +1 -0
  77. package/dist/postgres/index.js +7 -0
  78. package/dist/postgres/internal.d.ts +2 -0
  79. package/dist/postgres/internal.d.ts.map +1 -0
  80. package/dist/postgres/internal.js +1 -0
  81. package/dist/postgres/postgres.d.ts.map +1 -0
  82. package/dist/{backend-postgres → postgres}/postgres.js +0 -1
  83. package/dist/postgres/postgres.test.d.ts +2 -0
  84. package/dist/postgres/postgres.test.d.ts.map +1 -0
  85. package/dist/postgres/postgres.test.js +45 -0
  86. package/dist/postgres/scripts/db-migrate.d.ts.map +1 -0
  87. package/dist/{pg → postgres}/scripts/db-migrate.js +0 -1
  88. package/dist/postgres/scripts/db-reset.d.ts.map +1 -0
  89. package/dist/{pg → postgres}/scripts/db-reset.js +0 -1
  90. package/dist/{pg → postgres}/scripts/squawk.d.ts.map +1 -1
  91. package/dist/{pg → postgres}/scripts/squawk.js +0 -1
  92. package/dist/postgres/vitest.global-setup.d.ts.map +1 -0
  93. package/dist/{pg → postgres}/vitest.global-setup.js +0 -1
  94. package/dist/postgres.d.ts +2 -0
  95. package/dist/postgres.d.ts.map +1 -0
  96. package/dist/postgres.js +1 -0
  97. package/dist/registry.js +0 -1
  98. package/dist/registry.test.d.ts +2 -0
  99. package/dist/registry.test.d.ts.map +1 -0
  100. package/dist/registry.test.js +109 -0
  101. package/dist/sqlite/backend.d.ts +3 -1
  102. package/dist/sqlite/backend.d.ts.map +1 -1
  103. package/dist/sqlite/backend.js +5 -5
  104. package/dist/sqlite/backend.test.d.ts +2 -0
  105. package/dist/sqlite/backend.test.d.ts.map +1 -0
  106. package/dist/sqlite/backend.test.js +50 -0
  107. package/dist/sqlite/driver.d.ts +79 -0
  108. package/dist/sqlite/driver.d.ts.map +1 -0
  109. package/dist/sqlite/driver.js +62 -0
  110. package/dist/sqlite/index.d.ts +12 -2
  111. package/dist/sqlite/index.d.ts.map +1 -1
  112. package/dist/sqlite/index.js +11 -3
  113. package/dist/sqlite/internal.d.ts +2 -0
  114. package/dist/sqlite/internal.d.ts.map +1 -0
  115. package/dist/sqlite/internal.js +1 -0
  116. package/dist/sqlite/sqlite.js +0 -1
  117. package/dist/sqlite/sqlite.test.d.ts +2 -0
  118. package/dist/sqlite/sqlite.test.d.ts.map +1 -0
  119. package/dist/sqlite/sqlite.test.js +171 -0
  120. package/dist/sqlite.d.ts +2 -0
  121. package/dist/sqlite.d.ts.map +1 -0
  122. package/dist/sqlite.js +1 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -1
  124. package/dist/worker.js +0 -1
  125. package/dist/worker.test.d.ts +2 -0
  126. package/dist/worker.test.d.ts.map +1 -0
  127. package/dist/worker.test.js +900 -0
  128. package/dist/workflow.js +0 -1
  129. package/dist/workflow.test.d.ts +2 -0
  130. package/dist/workflow.test.d.ts.map +1 -0
  131. package/dist/workflow.test.js +84 -0
  132. package/package.json +19 -5
  133. package/dist/backend-postgres/index.d.ts +0 -44
  134. package/dist/backend-postgres/index.d.ts.map +0 -1
  135. package/dist/backend-postgres/index.js +0 -535
  136. package/dist/backend-postgres/index.js.map +0 -1
  137. package/dist/backend-postgres/postgres.d.ts.map +0 -1
  138. package/dist/backend-postgres/postgres.js.map +0 -1
  139. package/dist/backend-sqlite/index.d.ts.map +0 -1
  140. package/dist/backend-sqlite/index.js.map +0 -1
  141. package/dist/backend-sqlite/sqlite.d.ts.map +0 -1
  142. package/dist/backend-sqlite/sqlite.js.map +0 -1
  143. package/dist/backend.js.map +0 -1
  144. package/dist/bin/openworkflow.js.map +0 -1
  145. package/dist/client.js.map +0 -1
  146. package/dist/config.d.ts +0 -34
  147. package/dist/config.d.ts.map +0 -1
  148. package/dist/config.js +0 -49
  149. package/dist/config.js.map +0 -1
  150. package/dist/core/duration.js.map +0 -1
  151. package/dist/core/error.js.map +0 -1
  152. package/dist/core/json.js.map +0 -1
  153. package/dist/core/result.js.map +0 -1
  154. package/dist/core/retry.js.map +0 -1
  155. package/dist/core/schema.js.map +0 -1
  156. package/dist/core/step.js.map +0 -1
  157. package/dist/core/workflow.js.map +0 -1
  158. package/dist/execution.js.map +0 -1
  159. package/dist/index.js.map +0 -1
  160. package/dist/internal.js.map +0 -1
  161. package/dist/pg/backend.d.ts.map +0 -1
  162. package/dist/pg/backend.js.map +0 -1
  163. package/dist/pg/index.d.ts +0 -3
  164. package/dist/pg/index.d.ts.map +0 -1
  165. package/dist/pg/index.js +0 -3
  166. package/dist/pg/index.js.map +0 -1
  167. package/dist/pg/postgres.d.ts +0 -42
  168. package/dist/pg/postgres.d.ts.map +0 -1
  169. package/dist/pg/postgres.js +0 -234
  170. package/dist/pg/postgres.js.map +0 -1
  171. package/dist/pg/scripts/db-migrate.d.ts.map +0 -1
  172. package/dist/pg/scripts/db-migrate.js.map +0 -1
  173. package/dist/pg/scripts/db-reset.d.ts.map +0 -1
  174. package/dist/pg/scripts/db-reset.js.map +0 -1
  175. package/dist/pg/scripts/squawk.js.map +0 -1
  176. package/dist/pg/vitest.global-setup.d.ts.map +0 -1
  177. package/dist/pg/vitest.global-setup.js.map +0 -1
  178. package/dist/registry.js.map +0 -1
  179. package/dist/sqlite/backend.js.map +0 -1
  180. package/dist/sqlite/index.js.map +0 -1
  181. package/dist/sqlite/sqlite.js.map +0 -1
  182. package/dist/testing/backend.testsuite.d.ts.map +0 -1
  183. package/dist/testing/backend.testsuite.js.map +0 -1
  184. package/dist/testing/index.d.ts +0 -2
  185. package/dist/testing/index.d.ts.map +0 -1
  186. package/dist/testing/index.js.map +0 -1
  187. package/dist/worker.js.map +0 -1
  188. package/dist/workflow.js.map +0 -1
  189. /package/dist/{backend-postgres → postgres}/postgres.d.ts +0 -0
  190. /package/dist/{pg → postgres}/scripts/db-migrate.d.ts +0 -0
  191. /package/dist/{pg → postgres}/scripts/db-reset.d.ts +0 -0
  192. /package/dist/{pg → postgres}/scripts/squawk.d.ts +0 -0
  193. /package/dist/{pg → postgres}/vitest.global-setup.d.ts +0 -0
package/README.md CHANGED
@@ -8,16 +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
- Define a workflow in a few lines:
12
-
13
11
  ```ts
14
- import { BackendSqlite } from "@openworkflow/backend-sqlite";
15
- import { OpenWorkflow } from "openworkflow";
16
-
17
- const backend = BackendSqlite.connect("openworkflow/backend.db");
18
- const ow = new OpenWorkflow({ backend });
12
+ import { defineWorkflow } from "openworkflow";
19
13
 
20
- const sendWelcomeEmail = ow.defineWorkflow(
14
+ export const sendWelcomeEmail = defineWorkflow(
21
15
  { name: "send-welcome-email" },
22
16
  async ({ input, step }) => {
23
17
  const user = await step.run({ name: "fetch-user" }, async () => {
@@ -43,363 +37,62 @@ const sendWelcomeEmail = ow.defineWorkflow(
43
37
  );
44
38
  ```
45
39
 
46
- > OpenWorkflow is in active development and moving quickly. Check out the
47
- > [Roadmap](#roadmap) for what’s coming next.
48
-
49
40
  ## Quick Start
50
41
 
51
- Prerequisites:
52
-
53
- - Node.js
54
- - PostgreSQL (and/or SQLite)
42
+ **Prerequisites:** Node.js & PostgreSQL (or SQLite)
55
43
 
56
- ### 1. Install
57
-
58
- Install and set up OpenWorkflow with:
44
+ ### Install
59
45
 
60
46
  ```bash
61
47
  npx @openworkflow/cli init
62
48
  ```
63
49
 
64
- The CLI will prompt for your backend, installs dependencies, and generates:
65
- `openworkflow.config.ts`, `openworkflow/hello-world.ts`, `.env`, `.gitignore`,
66
- and a `worker` script.
67
-
68
- ### 2. Start a worker
69
-
70
- ```bash
71
- npm run worker
72
- # or
73
- npx @openworkflow/cli worker start
74
- ```
75
-
76
- This runs the worker using `openworkflow.config.ts` and auto-loads workflows
77
- from the configured directories (default: `openworkflow/`).
78
-
79
- ### 3. Run workflows from your app
80
-
81
- Edit the generated workflow file and run it from your application code using the
82
- OpenWorkflow client APIs (see examples below).
83
-
84
- That's it. Your workflow is now durable, resumable, and fault-tolerant.
85
-
86
- ## Core Concepts
87
-
88
- ### Workflows
89
-
90
- Workflows are durable functions. They can contain multiple steps, make external
91
- API calls, query databases, and perform complex logic. If a workflow is
92
- interrupted (crash, deploy, server restart), it resumes from its last completed
93
- step.
94
-
95
- ```ts
96
- const workflow = ow.defineWorkflow(
97
- { name: "my-workflow" },
98
- async ({ input, step }) => {
99
- // Your workflow logic here
100
- return result;
101
- },
102
- );
103
- ```
104
-
105
- ### Steps
106
-
107
- Steps are the building blocks of workflows. Each step is executed exactly once
108
- and its result is memoized. Steps let you break workflows into checkpoints.
109
-
110
- ```ts
111
- const result = await step.run({ name: "step-name" }, async () => {
112
- // This function runs once. If the workflow restarts,
113
- // this returns the cached result instead of re-running.
114
- return await someAsyncWork();
115
- });
116
- ```
117
-
118
- **Why steps matter**: Imagine a workflow that charges a credit card, then sends
119
- an email. Without steps, if your server crashes after charging the card, the
120
- workflow would retry from the beginning and charge the customer twice. With
121
- steps, the charge is memoized. The retry skips it and goes straight to sending
122
- the email.
123
-
124
- ### Workers
125
-
126
- Workers are long-running processes that poll your database for pending workflows
127
- and execute them. Run workers via the CLI so workflow discovery stays in sync
128
- with your `openworkflow.config.ts`:
129
-
130
- ```bash
131
- npm run worker
132
- # or
133
- npx @openworkflow/cli worker start
134
- ```
135
-
136
- Or, for more control, you can write your own workers:
137
-
138
- ```ts
139
- import { BackendSqlite } from "@openworkflow/backend-sqlite";
140
- import { OpenWorkflow } from "openworkflow";
141
-
142
- const backend = BackendSqlite.connect("openworkflow/backend.db");
143
- const ow = new OpenWorkflow({ backend });
144
-
145
- const worker = ow.newWorker({ concurrency: 20 });
146
- await worker.start();
147
-
148
- // & to shut down...
149
- await worker.stop(); // waits for in-flight workflows to complete
150
- ```
151
-
152
- Workers are stateless. They can be started, stopped, and deployed independently.
153
- Your database is the source of truth.
154
-
155
- ### How it Works
156
-
157
- 1. **Your app starts a workflow**: A row is inserted into the `workflow_runs`
158
- table with status `pending`.
159
- 2. **A worker picks it up**: The worker polls the database, claims the workflow,
160
- and sets its status to `running`.
161
- 3. **The worker executes steps**: Each step is recorded in the `step_attempts`
162
- table. If a step succeeds, its result is cached.
163
- 4. **The workflow completes**: The worker updates the `workflow_run` status to
164
- `completed` or `failed`.
165
- 5. **If the worker crashes**: The workflow becomes visible to other workers via
166
- a heartbeat timeout. Another worker picks it up, loads the cached step
167
- results, and resumes from the next step.
168
-
169
- ## Advanced Patterns
170
-
171
- ### Parallel Steps
172
-
173
- Run multiple steps concurrently using `Promise.all`:
174
-
175
- ```ts
176
- const [user, subscription, settings] = await Promise.all([
177
- step.run({ name: "fetch-user" }, async () => {
178
- await db.users.findOne({ id: input.userId });
179
- }),
180
- step.run({ name: "fetch-subscription" }, async () => {
181
- await stripe.subscriptions.retrieve(input.subId);
182
- }),
183
- step.run({ name: "fetch-settings" }, async () => {
184
- await db.settings.findOne({ userId: input.userId });
185
- }),
186
- ]);
187
- ```
188
-
189
- Each step is still memoized individually. If the workflow crashes mid-execution,
190
- completed steps return instantly on resume.
191
-
192
- ### Automatic Retries
193
-
194
- Steps can retry automatically with exponential backoff:
195
-
196
- ```ts
197
- const data = await step.run({ name: "fetch-external-api" }, async () => {
198
- // If this throws, the step retries automatically
199
- return await externalAPI.getData();
200
- });
201
- ```
202
-
203
- Configure retry behavior at the workflow or step level (coming soon) or handle
204
- errors explicitly in your step functions.
205
-
206
- ### Sleeping (Pausing) Workflows
207
-
208
- You can pause a workflow until a future time and, because sleeping releases the
209
- worker slot, you can pause thousands of workflows without tying up compute:
210
-
211
- ```ts
212
- // Pause for 1 hour (durable, non-blocking)
213
- await step.sleep("wait-one-hour", "1h");
214
- ```
215
-
216
- The sleep step is memoized after it completes. If the workflow is replayed again
217
- (e.g. due to a later retry) the completed sleep is not re-applied.
218
-
219
- #### Duration Formats
220
-
221
- Durations accept a number followed by a unit:
222
-
223
- | Unit | Aliases | Examples |
224
- | ------------ | --------------------- | ---------------- |
225
- | milliseconds | `ms`, `msec`, `msecs` | `100ms`, `1.5ms` |
226
- | seconds | `s`, `sec`, `secs` | `5s`, `0.25s` |
227
- | minutes | `m`, `min`, `mins` | `2m`, `1.5m` |
228
- | hours | `h`, `hr`, `hrs` | `1h`, `0.25h` |
229
- | days | `d`, `day(s)` | `1d`, `0.5d` |
230
- | weeks | `w`, `week(s)` | `1w`, `2w` |
231
- | months | `mo`, `month(s)` | `1mo`, `2mo` |
232
- | years | `y`, `yr`, `yrs` | `1y`, `2yr` |
233
-
234
- See more examples of accepted duration formats and aliases in the
235
- [tests](https://github.com/openworkflowdev/openworkflow/blob/main/packages/openworkflow/core/duration.test.ts).
236
-
237
- ### Type Safety
238
-
239
- Workflows are fully typed. Define input and output types for compile-time
240
- safety:
241
-
242
- ```ts
243
- interface ProcessOrderInput {
244
- orderId: string;
245
- userId: string;
246
- }
247
-
248
- interface ProcessOrderOutput {
249
- paymentId: string;
250
- shipmentId: string;
251
- }
252
-
253
- const processOrder = ow.defineWorkflow<ProcessOrderInput, ProcessOrderOutput>(
254
- { name: "process-order" },
255
- async ({ input, step }) => {
256
- // input is typed as ProcessOrderInput
257
- // return type must match ProcessOrderOutput
258
- return { paymentId: "...", shipmentId: "..." };
259
- },
260
- );
261
- ```
262
-
263
- ### Waiting for Results
264
-
265
- You can wait for a workflow to complete and get its result:
266
-
267
- ```ts
268
- const run = await myWorkflow.run({ data: "..." });
269
-
270
- // Wait for the workflow to finish (polls the database)
271
- const result = await run.result();
272
- ```
273
-
274
- ### Canceling Workflows
275
-
276
- You can cancel a workflow that is pending, running, or sleeping to prevent a
277
- workflow from continuing on to the next step:
278
-
279
- ```ts
280
- const handle = await myWorkflow.run({ data: "..." });
281
-
282
- // Cancel the workflow
283
- await handle.cancel();
284
- ```
285
-
286
- ### Workflow Versioning
287
-
288
- When you need to change workflow logic, use versioning for backwards
289
- compatibility.
290
-
291
- Define a workflow with an optional version:
292
-
293
- ```ts
294
- const workflow = ow.defineWorkflow(
295
- { name: "my-workflow", version: "v2" },
296
- async ({ input, step, version }) => {
297
- if (version === "v2") {
298
- // v2 runs go here
299
- await step.run({ name: "new-step" }, async () => {
300
- // legacy logic
301
- });
302
- } else {
303
- // v1 runs go here
304
- await step.run({ name: "old-step" }, async () => {
305
- // ...
306
- });
307
- }
308
- },
309
- );
310
- ```
311
-
312
- ### Validating Workflow Inputs
313
-
314
- You can require `.run()` callers to provide specific inputs by supplying a
315
- `schema` when defining the workflow. The schema is evaluated before the run is
316
- enqueued, so invalid requests fail immediately.
317
-
318
- ```ts
319
- import { z } from "zod";
320
-
321
- const summarizeDoc = ow.defineWorkflow(
322
- {
323
- name: "summarize",
324
- schema: z.object({
325
- docUrl: z.string().url(),
326
- }),
327
- },
328
- async ({ input, step }) => {
329
- // `input` has type { docUrl: string }
330
- },
331
- );
332
-
333
- // Throws before enqueueing the workflow because the input isn't a URL
334
- await summarizeDoc.run({ docUrl: "not-a-url" });
335
- ```
50
+ The CLI will guide you through setup and generate everything you need to get
51
+ started.
336
52
 
337
- Any validator function works as long as it throws on invalid data (great for
338
- custom logic or lightweight checks). Libraries such as Zod, ArkType, Valibot,
339
- and Yup.
53
+ For more details, check out our [docs](https://openworkflow.dev/docs).
340
54
 
341
- ## Production Checklist
55
+ ## Features
342
56
 
343
- - **Database**: Use a production-ready Postgres instance
344
- - **Workers**: Run at lease one worker process
345
- - **Concurrency**: Start with `concurrency: 10` per worker and tune based on
346
- your workload
347
- - **Monitoring**: Log worker activity and set up alerts for failed workflows
348
- - **Graceful Shutdown**: Handle `SIGTERM` to ensure clean deploys:
349
- ```ts
350
- process.on("SIGTERM", async () => {
351
- await worker.stop();
352
- process.exit(0);
353
- });
354
- ```
355
- - **Namespaces** (optional): Use `namespaceId` in your backend configuration to
356
- isolate workflows per environment:
357
- ```ts
358
- const backend = await BackendPostgres.connect(postgresUrl, {
359
- namespaceId: "production",
360
- });
361
- ```
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
362
67
 
363
- ## What's Next
68
+ ## Documentation
364
69
 
365
- - Read [ARCHITECTURE.md](./ARCHITECTURE.md) for a deep dive into how
366
- OpenWorkflow works
367
- - Check [examples/](./examples) for working examples
368
- - Star the repo and follow development on
369
- [GitHub](https://github.com/openworkflowdev/openworkflow)
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)
370
75
 
371
- ## Roadmap
76
+ ## Architecture
372
77
 
373
- **Live in current `npm` release:**
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.
374
81
 
375
- - ✅ PostgreSQL and SQLite backends
376
- - ✅ CLI (`npx @openworkflow/cli`)
377
- - ✅ Worker with concurrency control
378
- - ✅ Step memoization & retries
379
- - ✅ Graceful shutdown
380
- - ✅ Parallel step execution
381
- - ✅ Sleeping (pausing) workflows
382
- - ✅ Workflow versioning
383
- - ✅ Workflow cancelation
82
+ ## Examples
384
83
 
385
- **Coming Soon:**
84
+ Check out
85
+ [examples/](https://github.com/openworkflowdev/openworkflow/tree/main/examples)
86
+ for working examples.
386
87
 
387
- > These releases don't yet include a dashboard UI. For now, you can inspect
388
- > workflow and step state directly in PostgreSQL or SQLite (workflow_runs and
389
- > step_attempts tables). A dashboard is planned for an upcoming release to make
390
- > debugging and monitoring much easier.
88
+ ## Contributing
391
89
 
392
- - Dashboard UI
393
- - Idempotency keys
394
- - Rollback / compensation functions
395
- - Configurable retry policies
396
- - Signals for external events
397
- - Native OpenTelemetry integration
398
- - Additional backends (Redis)
399
- - 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.
400
93
 
401
- ## Bugs & feature requests
94
+ ## Community
402
95
 
403
- Found a bug or have a feature request? Please open an issue on GitHub so we can
404
- track and prioritize it:
405
- https://github.com/openworkflowdev/openworkflow/issues/new
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
@@ -1,4 +1,4 @@
1
- import type { Backend } from "openworkflow/internal";
1
+ import type { Backend } from "../backend.js";
2
2
  /**
3
3
  * Options for the Backend test suite.
4
4
  */
@@ -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"}
@@ -88,7 +88,7 @@ export function testBackend(options) {
88
88
  const second = await createPendingWorkflowRun(backend);
89
89
  const listed = await backend.listWorkflowRuns({});
90
90
  const listedIds = listed.data.map((run) => run.id);
91
- expect(listedIds).toEqual([first.id, second.id]);
91
+ expect(listedIds).toEqual([second.id, first.id]);
92
92
  await teardown(backend);
93
93
  });
94
94
  test("paginates workflow runs", async () => {
@@ -101,8 +101,8 @@ export function testBackend(options) {
101
101
  // p1
102
102
  const page1 = await backend.listWorkflowRuns({ limit: 2 });
103
103
  expect(page1.data).toHaveLength(2);
104
- expect(page1.data[0]?.id).toBe(runs[0]?.id);
105
- expect(page1.data[1]?.id).toBe(runs[1]?.id);
104
+ expect(page1.data[0]?.id).toBe(runs[4]?.id);
105
+ expect(page1.data[1]?.id).toBe(runs[3]?.id);
106
106
  expect(page1.pagination.next).not.toBeNull();
107
107
  expect(page1.pagination.prev).toBeNull();
108
108
  // p2
@@ -112,7 +112,7 @@ export function testBackend(options) {
112
112
  });
113
113
  expect(page2.data).toHaveLength(2);
114
114
  expect(page2.data[0]?.id).toBe(runs[2]?.id);
115
- expect(page2.data[1]?.id).toBe(runs[3]?.id);
115
+ expect(page2.data[1]?.id).toBe(runs[1]?.id);
116
116
  expect(page2.pagination.next).not.toBeNull();
117
117
  expect(page2.pagination.prev).not.toBeNull();
118
118
  // p3
@@ -121,7 +121,7 @@ export function testBackend(options) {
121
121
  after: page2.pagination.next, // eslint-disable-line @typescript-eslint/no-non-null-assertion
122
122
  });
123
123
  expect(page3.data).toHaveLength(1);
124
- expect(page3.data[0]?.id).toBe(runs[4]?.id);
124
+ expect(page3.data[0]?.id).toBe(runs[0]?.id);
125
125
  expect(page3.pagination.next).toBeNull();
126
126
  expect(page3.pagination.prev).not.toBeNull();
127
127
  // p2 again
@@ -131,7 +131,7 @@ export function testBackend(options) {
131
131
  });
132
132
  expect(page2Back.data).toHaveLength(2);
133
133
  expect(page2Back.data[0]?.id).toBe(runs[2]?.id);
134
- expect(page2Back.data[1]?.id).toBe(runs[3]?.id);
134
+ expect(page2Back.data[1]?.id).toBe(runs[1]?.id);
135
135
  expect(page2Back.pagination.next).toEqual(page2.pagination.next);
136
136
  expect(page2Back.pagination.prev).toEqual(page2.pagination.prev);
137
137
  await teardown(backend);
@@ -151,10 +151,10 @@ export function testBackend(options) {
151
151
  runs.push(await createPendingWorkflowRun(backend));
152
152
  }
153
153
  runs.sort((a, b) => {
154
- const timeDiff = a.createdAt.getTime() - b.createdAt.getTime();
154
+ const timeDiff = b.createdAt.getTime() - a.createdAt.getTime();
155
155
  if (timeDiff !== 0)
156
156
  return timeDiff;
157
- return a.id.localeCompare(b.id);
157
+ return b.id.localeCompare(a.id);
158
158
  });
159
159
  const page1 = await backend.listWorkflowRuns({ limit: 2 });
160
160
  expect(page1.data).toHaveLength(2);
@@ -1088,4 +1088,3 @@ function newDateInOneYear() {
1088
1088
  function sleep(ms) {
1089
1089
  return new Promise((resolve) => setTimeout(resolve, ms));
1090
1090
  }
1091
- //# sourceMappingURL=backend.testsuite.js.map
@@ -0,0 +1,2 @@
1
+ export { testBackend } from "./backend.testsuite.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../backend-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
@@ -1,2 +1 @@
1
1
  export { testBackend } from "./backend.testsuite.js";
2
- //# sourceMappingURL=index.js.map
package/dist/backend.js CHANGED
@@ -1,2 +1 @@
1
1
  export const DEFAULT_NAMESPACE_ID = "default";
2
- //# sourceMappingURL=backend.js.map
@@ -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.testsuite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAM5C;;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,CAgtC7D"}