@meridianjs/workflow-engine 0.1.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/dist/index.d.mts +126 -0
- package/dist/index.d.ts +126 -0
- package/dist/index.js +139 -0
- package/dist/index.mjs +107 -0
- package/package.json +40 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { StepContext, MeridianContainer } from '@meridianjs/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returned by a step's invoke function to provide a separate
|
|
5
|
+
* compensateInput (the data the compensate function receives on rollback).
|
|
6
|
+
*
|
|
7
|
+
* If the invoke function returns a plain value (not a StepResponse),
|
|
8
|
+
* that value is used as both the step output AND the compensate input.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* async function invoke(input, ctx) {
|
|
12
|
+
* const record = await svc.create(input)
|
|
13
|
+
* return new StepResponse(record, { id: record.id }) // slim compensate input
|
|
14
|
+
* }
|
|
15
|
+
* async function compensate({ id }, ctx) {
|
|
16
|
+
* await svc.delete(id)
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
19
|
+
declare class StepResponse<TOutput, TCompInput = TOutput> {
|
|
20
|
+
readonly output: TOutput;
|
|
21
|
+
readonly compensateInput: TCompInput;
|
|
22
|
+
constructor(output: TOutput, compensateInput: TCompInput);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Wraps the final return value of a workflow constructor function.
|
|
27
|
+
* The `output` becomes the `result` of the workflow run.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* createWorkflow("create-project", async (input) => {
|
|
31
|
+
* const project = await createProjectStep(input)
|
|
32
|
+
* return new WorkflowResponse(project)
|
|
33
|
+
* })
|
|
34
|
+
*/
|
|
35
|
+
declare class WorkflowResponse<T> {
|
|
36
|
+
readonly output: T;
|
|
37
|
+
constructor(output: T);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type InvokeFn<TInput, TOutput, TCompInput> = (input: TInput, ctx: StepContext) => Promise<TOutput | StepResponse<TOutput, TCompInput>>;
|
|
41
|
+
type CompensateFn<TCompInput> = (input: TCompInput, ctx: StepContext) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Defines a workflow step with an optional compensation (rollback) function.
|
|
44
|
+
*
|
|
45
|
+
* Returns a step function `(input: TInput) => Promise<TOutput>`.
|
|
46
|
+
* When called inside a workflow, automatically registers the compensation
|
|
47
|
+
* on the LIFO stack so it runs if a later step fails.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const createProjectStep = createStep(
|
|
51
|
+
* "create-project",
|
|
52
|
+
* async (input: CreateProjectInput, { container }) => {
|
|
53
|
+
* const svc = container.resolve("projectModuleService") as any
|
|
54
|
+
* const project = await svc.createProject(input)
|
|
55
|
+
* return new StepResponse(project, { projectId: project.id })
|
|
56
|
+
* },
|
|
57
|
+
* async ({ projectId }, { container }) => {
|
|
58
|
+
* const svc = container.resolve("projectModuleService") as any
|
|
59
|
+
* await svc.deleteProject(projectId)
|
|
60
|
+
* }
|
|
61
|
+
* )
|
|
62
|
+
*/
|
|
63
|
+
declare function createStep<TInput, TOutput, TCompInput = TOutput>(name: string, invoke: InvokeFn<TInput, TOutput, TCompInput>, compensate?: CompensateFn<TCompInput>): (input: TInput) => Promise<TOutput>;
|
|
64
|
+
|
|
65
|
+
type WorkflowTransactionStatus = "done" | "reverted" | "failed";
|
|
66
|
+
interface WorkflowResult<TOutput> {
|
|
67
|
+
result: TOutput;
|
|
68
|
+
errors: Error[];
|
|
69
|
+
transaction_status: WorkflowTransactionStatus;
|
|
70
|
+
}
|
|
71
|
+
interface WorkflowRunner<TInput, TOutput> {
|
|
72
|
+
run(opts: {
|
|
73
|
+
input: TInput;
|
|
74
|
+
}): Promise<WorkflowResult<TOutput>>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Defines a named workflow with a constructor function that orchestrates steps.
|
|
78
|
+
*
|
|
79
|
+
* Returns a factory `(container) => WorkflowRunner` so each request gets
|
|
80
|
+
* its own isolated run context with a fresh compensation stack.
|
|
81
|
+
*
|
|
82
|
+
* If any step throws, compensations run in LIFO order (saga pattern).
|
|
83
|
+
* Compensation errors are collected but do not re-throw.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* export const createProjectWorkflow = createWorkflow(
|
|
87
|
+
* "create-project",
|
|
88
|
+
* async (input: CreateProjectInput) => {
|
|
89
|
+
* const project = await createProjectStep(input)
|
|
90
|
+
* const _ = await logActivityStep({ entity_type: "project", entity_id: project.id })
|
|
91
|
+
* return new WorkflowResponse(project)
|
|
92
|
+
* }
|
|
93
|
+
* )
|
|
94
|
+
*
|
|
95
|
+
* // In a route handler:
|
|
96
|
+
* const { result, errors, transaction_status } = await createProjectWorkflow(req.scope).run({ input: req.body })
|
|
97
|
+
*/
|
|
98
|
+
declare function createWorkflow<TInput, TOutput>(name: string, constructorFn: (input: TInput) => Promise<WorkflowResponse<TOutput>>): (container: MeridianContainer) => WorkflowRunner<TInput, TOutput>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Transforms a step output value using a mapping function.
|
|
102
|
+
* A thin utility to make data transformations explicit in workflow constructors.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* const project = await createProjectStep(input)
|
|
106
|
+
* const activityInput = transform(project, (p) => ({
|
|
107
|
+
* entity_type: "project" as const,
|
|
108
|
+
* entity_id: p.id,
|
|
109
|
+
* workspace_id: p.workspace_id,
|
|
110
|
+
* }))
|
|
111
|
+
* await logActivityStep(activityInput)
|
|
112
|
+
*/
|
|
113
|
+
declare function transform<T, U>(input: T, fn: (value: T) => U): U;
|
|
114
|
+
/**
|
|
115
|
+
* Conditionally executes a step or block of code within a workflow.
|
|
116
|
+
* Returns undefined if the condition is false.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* const notification = await when(
|
|
120
|
+
* !!input.assignee_id,
|
|
121
|
+
* () => notifyAssigneeStep({ issue_id: issue.id, assignee_id: input.assignee_id! })
|
|
122
|
+
* )
|
|
123
|
+
*/
|
|
124
|
+
declare function when<T>(condition: boolean, fn: () => Promise<T>): Promise<T | undefined>;
|
|
125
|
+
|
|
126
|
+
export { StepResponse, WorkflowResponse, type WorkflowResult, type WorkflowRunner, type WorkflowTransactionStatus, createStep, createWorkflow, transform, when };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { StepContext, MeridianContainer } from '@meridianjs/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returned by a step's invoke function to provide a separate
|
|
5
|
+
* compensateInput (the data the compensate function receives on rollback).
|
|
6
|
+
*
|
|
7
|
+
* If the invoke function returns a plain value (not a StepResponse),
|
|
8
|
+
* that value is used as both the step output AND the compensate input.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* async function invoke(input, ctx) {
|
|
12
|
+
* const record = await svc.create(input)
|
|
13
|
+
* return new StepResponse(record, { id: record.id }) // slim compensate input
|
|
14
|
+
* }
|
|
15
|
+
* async function compensate({ id }, ctx) {
|
|
16
|
+
* await svc.delete(id)
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
19
|
+
declare class StepResponse<TOutput, TCompInput = TOutput> {
|
|
20
|
+
readonly output: TOutput;
|
|
21
|
+
readonly compensateInput: TCompInput;
|
|
22
|
+
constructor(output: TOutput, compensateInput: TCompInput);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Wraps the final return value of a workflow constructor function.
|
|
27
|
+
* The `output` becomes the `result` of the workflow run.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* createWorkflow("create-project", async (input) => {
|
|
31
|
+
* const project = await createProjectStep(input)
|
|
32
|
+
* return new WorkflowResponse(project)
|
|
33
|
+
* })
|
|
34
|
+
*/
|
|
35
|
+
declare class WorkflowResponse<T> {
|
|
36
|
+
readonly output: T;
|
|
37
|
+
constructor(output: T);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type InvokeFn<TInput, TOutput, TCompInput> = (input: TInput, ctx: StepContext) => Promise<TOutput | StepResponse<TOutput, TCompInput>>;
|
|
41
|
+
type CompensateFn<TCompInput> = (input: TCompInput, ctx: StepContext) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Defines a workflow step with an optional compensation (rollback) function.
|
|
44
|
+
*
|
|
45
|
+
* Returns a step function `(input: TInput) => Promise<TOutput>`.
|
|
46
|
+
* When called inside a workflow, automatically registers the compensation
|
|
47
|
+
* on the LIFO stack so it runs if a later step fails.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const createProjectStep = createStep(
|
|
51
|
+
* "create-project",
|
|
52
|
+
* async (input: CreateProjectInput, { container }) => {
|
|
53
|
+
* const svc = container.resolve("projectModuleService") as any
|
|
54
|
+
* const project = await svc.createProject(input)
|
|
55
|
+
* return new StepResponse(project, { projectId: project.id })
|
|
56
|
+
* },
|
|
57
|
+
* async ({ projectId }, { container }) => {
|
|
58
|
+
* const svc = container.resolve("projectModuleService") as any
|
|
59
|
+
* await svc.deleteProject(projectId)
|
|
60
|
+
* }
|
|
61
|
+
* )
|
|
62
|
+
*/
|
|
63
|
+
declare function createStep<TInput, TOutput, TCompInput = TOutput>(name: string, invoke: InvokeFn<TInput, TOutput, TCompInput>, compensate?: CompensateFn<TCompInput>): (input: TInput) => Promise<TOutput>;
|
|
64
|
+
|
|
65
|
+
type WorkflowTransactionStatus = "done" | "reverted" | "failed";
|
|
66
|
+
interface WorkflowResult<TOutput> {
|
|
67
|
+
result: TOutput;
|
|
68
|
+
errors: Error[];
|
|
69
|
+
transaction_status: WorkflowTransactionStatus;
|
|
70
|
+
}
|
|
71
|
+
interface WorkflowRunner<TInput, TOutput> {
|
|
72
|
+
run(opts: {
|
|
73
|
+
input: TInput;
|
|
74
|
+
}): Promise<WorkflowResult<TOutput>>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Defines a named workflow with a constructor function that orchestrates steps.
|
|
78
|
+
*
|
|
79
|
+
* Returns a factory `(container) => WorkflowRunner` so each request gets
|
|
80
|
+
* its own isolated run context with a fresh compensation stack.
|
|
81
|
+
*
|
|
82
|
+
* If any step throws, compensations run in LIFO order (saga pattern).
|
|
83
|
+
* Compensation errors are collected but do not re-throw.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* export const createProjectWorkflow = createWorkflow(
|
|
87
|
+
* "create-project",
|
|
88
|
+
* async (input: CreateProjectInput) => {
|
|
89
|
+
* const project = await createProjectStep(input)
|
|
90
|
+
* const _ = await logActivityStep({ entity_type: "project", entity_id: project.id })
|
|
91
|
+
* return new WorkflowResponse(project)
|
|
92
|
+
* }
|
|
93
|
+
* )
|
|
94
|
+
*
|
|
95
|
+
* // In a route handler:
|
|
96
|
+
* const { result, errors, transaction_status } = await createProjectWorkflow(req.scope).run({ input: req.body })
|
|
97
|
+
*/
|
|
98
|
+
declare function createWorkflow<TInput, TOutput>(name: string, constructorFn: (input: TInput) => Promise<WorkflowResponse<TOutput>>): (container: MeridianContainer) => WorkflowRunner<TInput, TOutput>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Transforms a step output value using a mapping function.
|
|
102
|
+
* A thin utility to make data transformations explicit in workflow constructors.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* const project = await createProjectStep(input)
|
|
106
|
+
* const activityInput = transform(project, (p) => ({
|
|
107
|
+
* entity_type: "project" as const,
|
|
108
|
+
* entity_id: p.id,
|
|
109
|
+
* workspace_id: p.workspace_id,
|
|
110
|
+
* }))
|
|
111
|
+
* await logActivityStep(activityInput)
|
|
112
|
+
*/
|
|
113
|
+
declare function transform<T, U>(input: T, fn: (value: T) => U): U;
|
|
114
|
+
/**
|
|
115
|
+
* Conditionally executes a step or block of code within a workflow.
|
|
116
|
+
* Returns undefined if the condition is false.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* const notification = await when(
|
|
120
|
+
* !!input.assignee_id,
|
|
121
|
+
* () => notifyAssigneeStep({ issue_id: issue.id, assignee_id: input.assignee_id! })
|
|
122
|
+
* )
|
|
123
|
+
*/
|
|
124
|
+
declare function when<T>(condition: boolean, fn: () => Promise<T>): Promise<T | undefined>;
|
|
125
|
+
|
|
126
|
+
export { StepResponse, WorkflowResponse, type WorkflowResult, type WorkflowRunner, type WorkflowTransactionStatus, createStep, createWorkflow, transform, when };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
StepResponse: () => StepResponse,
|
|
24
|
+
WorkflowResponse: () => WorkflowResponse,
|
|
25
|
+
createStep: () => createStep,
|
|
26
|
+
createWorkflow: () => createWorkflow,
|
|
27
|
+
transform: () => transform,
|
|
28
|
+
when: () => when
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/step-response.ts
|
|
33
|
+
var StepResponse = class {
|
|
34
|
+
constructor(output, compensateInput) {
|
|
35
|
+
this.output = output;
|
|
36
|
+
this.compensateInput = compensateInput;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/workflow-response.ts
|
|
41
|
+
var WorkflowResponse = class {
|
|
42
|
+
constructor(output) {
|
|
43
|
+
this.output = output;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/run-context.ts
|
|
48
|
+
var import_node_async_hooks = require("async_hooks");
|
|
49
|
+
var workflowRunContext = new import_node_async_hooks.AsyncLocalStorage();
|
|
50
|
+
function getWorkflowRunContext() {
|
|
51
|
+
const ctx = workflowRunContext.getStore();
|
|
52
|
+
if (!ctx) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
"No active workflow context. Steps must be called inside a createWorkflow constructor function."
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return ctx;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/create-step.ts
|
|
61
|
+
function createStep(name, invoke, compensate) {
|
|
62
|
+
return async (input) => {
|
|
63
|
+
const runCtx = getWorkflowRunContext();
|
|
64
|
+
const stepCtx = { container: runCtx.container };
|
|
65
|
+
const result = await invoke(input, stepCtx);
|
|
66
|
+
let output;
|
|
67
|
+
let compInput;
|
|
68
|
+
if (result instanceof StepResponse) {
|
|
69
|
+
output = result.output;
|
|
70
|
+
compInput = result.compensateInput;
|
|
71
|
+
} else {
|
|
72
|
+
output = result;
|
|
73
|
+
compInput = result;
|
|
74
|
+
}
|
|
75
|
+
if (compensate) {
|
|
76
|
+
const capturedCompInput = compInput;
|
|
77
|
+
runCtx.compensationStack.push(
|
|
78
|
+
async () => compensate(capturedCompInput, stepCtx)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return output;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/create-workflow.ts
|
|
86
|
+
function createWorkflow(name, constructorFn) {
|
|
87
|
+
return (container) => ({
|
|
88
|
+
async run({ input }) {
|
|
89
|
+
const context = {
|
|
90
|
+
container,
|
|
91
|
+
compensationStack: []
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
const response = await workflowRunContext.run(
|
|
95
|
+
context,
|
|
96
|
+
() => constructorFn(input)
|
|
97
|
+
);
|
|
98
|
+
return {
|
|
99
|
+
result: response.output,
|
|
100
|
+
errors: [],
|
|
101
|
+
transaction_status: "done"
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const errors = [error];
|
|
105
|
+
const stack = [...context.compensationStack].reverse();
|
|
106
|
+
for (const compensate of stack) {
|
|
107
|
+
try {
|
|
108
|
+
await compensate();
|
|
109
|
+
} catch (compError) {
|
|
110
|
+
errors.push(compError);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
result: void 0,
|
|
115
|
+
errors,
|
|
116
|
+
transaction_status: "reverted"
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/transform.ts
|
|
124
|
+
function transform(input, fn) {
|
|
125
|
+
return fn(input);
|
|
126
|
+
}
|
|
127
|
+
async function when(condition, fn) {
|
|
128
|
+
if (condition) return fn();
|
|
129
|
+
return void 0;
|
|
130
|
+
}
|
|
131
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
132
|
+
0 && (module.exports = {
|
|
133
|
+
StepResponse,
|
|
134
|
+
WorkflowResponse,
|
|
135
|
+
createStep,
|
|
136
|
+
createWorkflow,
|
|
137
|
+
transform,
|
|
138
|
+
when
|
|
139
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/step-response.ts
|
|
2
|
+
var StepResponse = class {
|
|
3
|
+
constructor(output, compensateInput) {
|
|
4
|
+
this.output = output;
|
|
5
|
+
this.compensateInput = compensateInput;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// src/workflow-response.ts
|
|
10
|
+
var WorkflowResponse = class {
|
|
11
|
+
constructor(output) {
|
|
12
|
+
this.output = output;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/run-context.ts
|
|
17
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
18
|
+
var workflowRunContext = new AsyncLocalStorage();
|
|
19
|
+
function getWorkflowRunContext() {
|
|
20
|
+
const ctx = workflowRunContext.getStore();
|
|
21
|
+
if (!ctx) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"No active workflow context. Steps must be called inside a createWorkflow constructor function."
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
return ctx;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/create-step.ts
|
|
30
|
+
function createStep(name, invoke, compensate) {
|
|
31
|
+
return async (input) => {
|
|
32
|
+
const runCtx = getWorkflowRunContext();
|
|
33
|
+
const stepCtx = { container: runCtx.container };
|
|
34
|
+
const result = await invoke(input, stepCtx);
|
|
35
|
+
let output;
|
|
36
|
+
let compInput;
|
|
37
|
+
if (result instanceof StepResponse) {
|
|
38
|
+
output = result.output;
|
|
39
|
+
compInput = result.compensateInput;
|
|
40
|
+
} else {
|
|
41
|
+
output = result;
|
|
42
|
+
compInput = result;
|
|
43
|
+
}
|
|
44
|
+
if (compensate) {
|
|
45
|
+
const capturedCompInput = compInput;
|
|
46
|
+
runCtx.compensationStack.push(
|
|
47
|
+
async () => compensate(capturedCompInput, stepCtx)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return output;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/create-workflow.ts
|
|
55
|
+
function createWorkflow(name, constructorFn) {
|
|
56
|
+
return (container) => ({
|
|
57
|
+
async run({ input }) {
|
|
58
|
+
const context = {
|
|
59
|
+
container,
|
|
60
|
+
compensationStack: []
|
|
61
|
+
};
|
|
62
|
+
try {
|
|
63
|
+
const response = await workflowRunContext.run(
|
|
64
|
+
context,
|
|
65
|
+
() => constructorFn(input)
|
|
66
|
+
);
|
|
67
|
+
return {
|
|
68
|
+
result: response.output,
|
|
69
|
+
errors: [],
|
|
70
|
+
transaction_status: "done"
|
|
71
|
+
};
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const errors = [error];
|
|
74
|
+
const stack = [...context.compensationStack].reverse();
|
|
75
|
+
for (const compensate of stack) {
|
|
76
|
+
try {
|
|
77
|
+
await compensate();
|
|
78
|
+
} catch (compError) {
|
|
79
|
+
errors.push(compError);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
result: void 0,
|
|
84
|
+
errors,
|
|
85
|
+
transaction_status: "reverted"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/transform.ts
|
|
93
|
+
function transform(input, fn) {
|
|
94
|
+
return fn(input);
|
|
95
|
+
}
|
|
96
|
+
async function when(condition, fn) {
|
|
97
|
+
if (condition) return fn();
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
StepResponse,
|
|
102
|
+
WorkflowResponse,
|
|
103
|
+
createStep,
|
|
104
|
+
createWorkflow,
|
|
105
|
+
transform,
|
|
106
|
+
when
|
|
107
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meridianjs/workflow-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Meridian workflow engine — DAG runner with LIFO saga compensation",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
22
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"clean": "rm -rf dist",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@meridianjs/types": "^0.1.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "^8.3.5",
|
|
32
|
+
"typescript": "*"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
40
|
+
}
|