@meridianjs/workflow-engine 0.1.0 → 1.0.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 +129 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @meridianjs/workflow-engine
|
|
2
|
+
|
|
3
|
+
DAG-based workflow engine for MeridianJS with LIFO saga compensation. Define multi-step operations where each step declares a rollback function — if any step fails, all previously completed steps are automatically compensated in reverse order.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @meridianjs/workflow-engine
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Workflows are used for any operation that touches multiple services or needs transactional guarantees. Every mutation route in a MeridianJS application runs through a workflow.
|
|
14
|
+
|
|
15
|
+
## Core Concepts
|
|
16
|
+
|
|
17
|
+
### Steps
|
|
18
|
+
|
|
19
|
+
A step is the atomic unit of a workflow. It has a **forward** function (the operation) and an optional **compensation** function (the rollback):
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { createStep, StepResponse } from "@meridianjs/workflow-engine"
|
|
23
|
+
import type { MeridianContainer } from "@meridianjs/types"
|
|
24
|
+
|
|
25
|
+
const createProjectStep = createStep(
|
|
26
|
+
"create-project",
|
|
27
|
+
async (input: { name: string; workspace_id: string }, { container }: { container: MeridianContainer }) => {
|
|
28
|
+
const svc = container.resolve("projectModuleService") as any
|
|
29
|
+
const project = await svc.createProject(input)
|
|
30
|
+
return new StepResponse({ project }, { projectId: project.id })
|
|
31
|
+
},
|
|
32
|
+
// Compensation — called if a later step fails
|
|
33
|
+
async ({ projectId }: { projectId: string }, { container }: { container: MeridianContainer }) => {
|
|
34
|
+
const svc = container.resolve("projectModuleService") as any
|
|
35
|
+
await svc.deleteProject(projectId)
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`StepResponse` takes two arguments: the **output** (passed to the next step) and the **compensation data** (passed back to the compensation function if needed).
|
|
41
|
+
|
|
42
|
+
### Workflows
|
|
43
|
+
|
|
44
|
+
Wire steps together into a named workflow:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { createWorkflow, WorkflowResponse } from "@meridianjs/workflow-engine"
|
|
48
|
+
|
|
49
|
+
interface CreateProjectInput {
|
|
50
|
+
name: string
|
|
51
|
+
workspace_id: string
|
|
52
|
+
identifier?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const createProjectWorkflow = createWorkflow(
|
|
56
|
+
"create-project",
|
|
57
|
+
(input: CreateProjectInput) => {
|
|
58
|
+
const projectResult = createProjectStep(input)
|
|
59
|
+
const statusResult = seedDefaultStatusesStep(projectResult)
|
|
60
|
+
return new WorkflowResponse(statusResult)
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Running a Workflow
|
|
66
|
+
|
|
67
|
+
Call the workflow from a route handler using `req.scope` as the container:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
export const POST = async (req: any, res: Response) => {
|
|
71
|
+
const { result, errors, transaction_status } = await createProjectWorkflow(req.scope).run({
|
|
72
|
+
input: req.body,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
if (transaction_status === "reverted") {
|
|
76
|
+
res.status(500).json({ error: { message: errors[0].message } })
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
res.status(201).json({ project: result.project })
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| `transaction_status` | Meaning |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `"done"` | All steps completed successfully |
|
|
87
|
+
| `"reverted"` | A step failed; compensation ran for all previous steps |
|
|
88
|
+
|
|
89
|
+
### Conditional Branching
|
|
90
|
+
|
|
91
|
+
Use `when()` to execute steps conditionally:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { when } from "@meridianjs/workflow-engine"
|
|
95
|
+
|
|
96
|
+
const workflowResult = createWorkflow("my-workflow", (input: Input) => {
|
|
97
|
+
const base = createBaseStep(input)
|
|
98
|
+
const extra = when(base, (data) => data.needsExtra === true, () => extraStep(base))
|
|
99
|
+
return new WorkflowResponse(extra)
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Data Transformation
|
|
104
|
+
|
|
105
|
+
Use `transform()` to reshape step output before passing it to the next step:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { transform } from "@meridianjs/workflow-engine"
|
|
109
|
+
|
|
110
|
+
const shaped = transform(stepResult, (data) => ({
|
|
111
|
+
projectId: data.project.id,
|
|
112
|
+
workspaceId: data.project.workspace_id,
|
|
113
|
+
}))
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## API Reference
|
|
117
|
+
|
|
118
|
+
| Export | Description |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `createStep(name, forward, compensate?)` | Define a step with optional rollback |
|
|
121
|
+
| `createWorkflow(name, builder)` | Define a workflow from a step composition function |
|
|
122
|
+
| `StepResponse(output, compensationData?)` | Wrap step output + compensation payload |
|
|
123
|
+
| `WorkflowResponse(stepResult)` | Declare the workflow's final return value |
|
|
124
|
+
| `transform(input, fn)` | Reshape step output |
|
|
125
|
+
| `when(input, condition, fn)` | Conditionally run a step |
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meridianjs/workflow-engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Meridian workflow engine — DAG runner with LIFO saga compensation",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"prepublishOnly": "npm run build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@meridianjs/types": "^
|
|
28
|
+
"@meridianjs/types": "^1.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"tsup": "^8.3.5",
|