@yigitahmetsahin/workflow-ts 1.2.1 → 2.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/README.md +116 -4
- package/dist/index.cjs +49 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -10
- package/dist/index.d.ts +52 -10
- package/dist/index.js +46 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A simple, extensible TypeScript workflow engine supporting serial and parallel w
|
|
|
11
11
|
|
|
12
12
|
- 🔄 **Serial & Parallel Execution** - Chain work items sequentially or run them concurrently
|
|
13
13
|
- 🎯 **Full Type Inference** - Work names and result types are automatically inferred
|
|
14
|
+
- 🧩 **Standalone Work Definitions** - Define works as reusable `Work` instances
|
|
14
15
|
- ⏭️ **Conditional Execution** - Skip work items based on runtime conditions
|
|
15
16
|
- 🛡️ **Error Handling** - Built-in error callbacks and workflow failure states
|
|
16
17
|
- 📊 **Execution Tracking** - Duration tracking for individual works and total workflow
|
|
@@ -33,8 +34,9 @@ pnpm add @yigitahmetsahin/workflow-ts
|
|
|
33
34
|
## Quick Start
|
|
34
35
|
|
|
35
36
|
```typescript
|
|
36
|
-
import { Workflow, WorkflowStatus } from '@yigitahmetsahin/workflow-ts';
|
|
37
|
+
import { Workflow, Work, WorkflowStatus } from '@yigitahmetsahin/workflow-ts';
|
|
37
38
|
|
|
39
|
+
// Option 1: Define works inline
|
|
38
40
|
const workflow = new Workflow<{ userId: string }>()
|
|
39
41
|
.serial({
|
|
40
42
|
name: 'validate',
|
|
@@ -54,17 +56,31 @@ const workflow = new Workflow<{ userId: string }>()
|
|
|
54
56
|
name: 'process',
|
|
55
57
|
execute: async (ctx) => {
|
|
56
58
|
// ✅ Types are automatically inferred!
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
+
// workResults.get() returns IWorkResult with status, result, duration
|
|
60
|
+
const orders = ctx.workResults.get('fetchOrders').result; // { id: number }[]
|
|
61
|
+
const profile = ctx.workResults.get('fetchProfile').result; // { name: string; email: string }
|
|
59
62
|
return { orderCount: orders?.length ?? 0, userName: profile?.name };
|
|
60
63
|
},
|
|
61
64
|
});
|
|
62
65
|
|
|
66
|
+
// Option 2: Define works as reusable Work instances
|
|
67
|
+
const validateUser = new Work({
|
|
68
|
+
name: 'validate',
|
|
69
|
+
execute: async (ctx) => ctx.data.userId.length > 0,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const fetchOrders = new Work({
|
|
73
|
+
name: 'fetchOrders',
|
|
74
|
+
execute: async (ctx) => [{ id: 1 }, { id: 2 }],
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const workflow2 = new Workflow<{ userId: string }>().serial(validateUser).parallel([fetchOrders]);
|
|
78
|
+
|
|
63
79
|
const result = await workflow.run({ userId: 'user-123' });
|
|
64
80
|
|
|
65
81
|
if (result.status === WorkflowStatus.COMPLETED) {
|
|
66
82
|
console.log('Workflow completed in', result.totalDuration, 'ms');
|
|
67
|
-
console.log('Final result:', result.context.workResults.get('process'));
|
|
83
|
+
console.log('Final result:', result.context.workResults.get('process').result);
|
|
68
84
|
}
|
|
69
85
|
```
|
|
70
86
|
|
|
@@ -109,6 +125,62 @@ workflow.parallel([
|
|
|
109
125
|
]);
|
|
110
126
|
```
|
|
111
127
|
|
|
128
|
+
### `Work` Class
|
|
129
|
+
|
|
130
|
+
Define standalone, reusable work units using the `Work` class:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { Work, Workflow } from '@yigitahmetsahin/workflow-ts';
|
|
134
|
+
|
|
135
|
+
// Define works as standalone units
|
|
136
|
+
const fetchUser = new Work({
|
|
137
|
+
name: 'fetchUser',
|
|
138
|
+
execute: async (ctx) => {
|
|
139
|
+
const response = await fetch(`/api/users/${ctx.data.userId}`);
|
|
140
|
+
return response.json();
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const fetchOrders = new Work({
|
|
145
|
+
name: 'fetchOrders',
|
|
146
|
+
execute: async (ctx) => {
|
|
147
|
+
const response = await fetch(`/api/orders?userId=${ctx.data.userId}`);
|
|
148
|
+
return response.json();
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Use them in workflows
|
|
153
|
+
const workflow = new Workflow<{ userId: string }>()
|
|
154
|
+
.serial(fetchUser)
|
|
155
|
+
.parallel([fetchOrders, anotherWork]);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Works can be mixed with inline definitions:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
workflow
|
|
162
|
+
.serial(fetchUser) // Work instance
|
|
163
|
+
.parallel([
|
|
164
|
+
fetchOrders, // Work instance
|
|
165
|
+
{
|
|
166
|
+
// Inline definition
|
|
167
|
+
name: 'fetchProfile',
|
|
168
|
+
execute: async (ctx) => ({ name: 'John' }),
|
|
169
|
+
},
|
|
170
|
+
]);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The `Work` class supports all the same options as inline definitions:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const conditionalWork = new Work({
|
|
177
|
+
name: 'conditionalTask',
|
|
178
|
+
execute: async (ctx) => 'result',
|
|
179
|
+
shouldRun: (ctx) => ctx.data.enabled, // Optional condition
|
|
180
|
+
onError: (error, ctx) => console.error(error), // Optional error handler
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
112
184
|
### `.run(initialData)`
|
|
113
185
|
|
|
114
186
|
Execute the workflow with initial data.
|
|
@@ -130,8 +202,37 @@ interface IWorkflowResult {
|
|
|
130
202
|
totalDuration: number; // Total execution time in ms
|
|
131
203
|
error?: Error; // Error if workflow failed
|
|
132
204
|
}
|
|
205
|
+
|
|
206
|
+
// Each work result contains execution details
|
|
207
|
+
interface IWorkResult<T> {
|
|
208
|
+
status: WorkStatus; // 'completed' | 'failed' | 'skipped'
|
|
209
|
+
result?: T; // The return value from execute()
|
|
210
|
+
error?: Error; // Error if work failed
|
|
211
|
+
duration: number; // Execution time in ms
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Accessing Work Results
|
|
216
|
+
|
|
217
|
+
`ctx.workResults.get()` returns an `IWorkResult` object, not the raw value:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Get the full work result with metadata
|
|
221
|
+
const workResult = ctx.workResults.get('fetchUser');
|
|
222
|
+
console.log(workResult.status); // 'completed' | 'failed' | 'skipped'
|
|
223
|
+
console.log(workResult.duration); // execution time in ms
|
|
224
|
+
|
|
225
|
+
// Get just the return value
|
|
226
|
+
const user = ctx.workResults.get('fetchUser').result;
|
|
227
|
+
|
|
228
|
+
// Check status before accessing result
|
|
229
|
+
if (workResult.status === WorkStatus.COMPLETED) {
|
|
230
|
+
console.log('User:', workResult.result);
|
|
231
|
+
}
|
|
133
232
|
```
|
|
134
233
|
|
|
234
|
+
> **Note:** `workResults.get()` throws an error if called for a work that hasn't executed yet. Use `workResults.has()` to check if a result exists.
|
|
235
|
+
|
|
135
236
|
## Conditional Execution
|
|
136
237
|
|
|
137
238
|
Skip works based on runtime conditions:
|
|
@@ -147,6 +248,17 @@ workflow.serial({
|
|
|
147
248
|
});
|
|
148
249
|
```
|
|
149
250
|
|
|
251
|
+
Skipped works are still accessible via `workResults.get()` with `status: 'skipped'`:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
const emailResult = ctx.workResults.get('sendEmail');
|
|
255
|
+
if (emailResult.status === WorkStatus.SKIPPED) {
|
|
256
|
+
console.log('Email was skipped');
|
|
257
|
+
} else if (emailResult.status === WorkStatus.COMPLETED) {
|
|
258
|
+
console.log('Email sent:', emailResult.result);
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
150
262
|
## Error Handling
|
|
151
263
|
|
|
152
264
|
Handle errors at the work level:
|
package/dist/index.cjs
CHANGED
|
@@ -20,9 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
Work: () => Work,
|
|
23
24
|
WorkStatus: () => WorkStatus,
|
|
24
25
|
Workflow: () => Workflow,
|
|
25
|
-
WorkflowStatus: () => WorkflowStatus
|
|
26
|
+
WorkflowStatus: () => WorkflowStatus,
|
|
27
|
+
getWorkDefinition: () => getWorkDefinition
|
|
26
28
|
});
|
|
27
29
|
module.exports = __toCommonJS(index_exports);
|
|
28
30
|
|
|
@@ -43,13 +45,32 @@ var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
|
|
|
43
45
|
return WorkflowStatus2;
|
|
44
46
|
})(WorkflowStatus || {});
|
|
45
47
|
|
|
48
|
+
// src/work.ts
|
|
49
|
+
var Work = class {
|
|
50
|
+
constructor(definition) {
|
|
51
|
+
this.name = definition.name;
|
|
52
|
+
this.execute = definition.execute;
|
|
53
|
+
this.shouldRun = definition.shouldRun;
|
|
54
|
+
this.onError = definition.onError;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
function getWorkDefinition(input) {
|
|
58
|
+
return input;
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
// src/workflow.ts
|
|
47
62
|
var WorkResultsMap = class {
|
|
48
63
|
constructor() {
|
|
49
64
|
this.map = /* @__PURE__ */ new Map();
|
|
50
65
|
}
|
|
51
66
|
get(name) {
|
|
52
|
-
|
|
67
|
+
const result = this.map.get(name);
|
|
68
|
+
if (!result) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Work result "${String(name)}" not found. This work may not have executed yet.`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
53
74
|
}
|
|
54
75
|
set(name, value) {
|
|
55
76
|
this.map.set(name, value);
|
|
@@ -64,23 +85,25 @@ var Workflow = class {
|
|
|
64
85
|
}
|
|
65
86
|
/**
|
|
66
87
|
* Add a serial work to the workflow.
|
|
88
|
+
* Accepts either an inline work definition or a Work instance.
|
|
67
89
|
* The work name and result type are automatically inferred.
|
|
68
90
|
*/
|
|
69
91
|
serial(work) {
|
|
70
92
|
this.works.push({
|
|
71
93
|
type: "serial",
|
|
72
|
-
works: [work]
|
|
94
|
+
works: [getWorkDefinition(work)]
|
|
73
95
|
});
|
|
74
96
|
return this;
|
|
75
97
|
}
|
|
76
98
|
/**
|
|
77
99
|
* Add parallel works to the workflow.
|
|
100
|
+
* Accepts either inline work definitions or Work instances.
|
|
78
101
|
* All work names and result types are automatically inferred.
|
|
79
102
|
*/
|
|
80
103
|
parallel(works) {
|
|
81
104
|
this.works.push({
|
|
82
105
|
type: "parallel",
|
|
83
|
-
works
|
|
106
|
+
works: works.map((w) => getWorkDefinition(w))
|
|
84
107
|
});
|
|
85
108
|
return this;
|
|
86
109
|
}
|
|
@@ -126,21 +149,24 @@ var Workflow = class {
|
|
|
126
149
|
if (work.shouldRun) {
|
|
127
150
|
const shouldRun = await work.shouldRun(context);
|
|
128
151
|
if (!shouldRun) {
|
|
129
|
-
|
|
152
|
+
const skippedResult = {
|
|
130
153
|
status: "skipped" /* SKIPPED */,
|
|
131
154
|
duration: Date.now() - workStartTime
|
|
132
|
-
}
|
|
155
|
+
};
|
|
156
|
+
context.workResults.set(work.name, skippedResult);
|
|
157
|
+
workResults.set(work.name, skippedResult);
|
|
133
158
|
return;
|
|
134
159
|
}
|
|
135
160
|
}
|
|
136
161
|
try {
|
|
137
162
|
const result = await work.execute(context);
|
|
138
|
-
|
|
139
|
-
workResults.set(work.name, {
|
|
163
|
+
const workResult = {
|
|
140
164
|
status: "completed" /* COMPLETED */,
|
|
141
165
|
result,
|
|
142
166
|
duration: Date.now() - workStartTime
|
|
143
|
-
}
|
|
167
|
+
};
|
|
168
|
+
context.workResults.set(work.name, workResult);
|
|
169
|
+
workResults.set(work.name, workResult);
|
|
144
170
|
} catch (error) {
|
|
145
171
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
146
172
|
workResults.set(work.name, {
|
|
@@ -163,10 +189,12 @@ var Workflow = class {
|
|
|
163
189
|
if (work.shouldRun) {
|
|
164
190
|
const shouldRun = await work.shouldRun(context);
|
|
165
191
|
if (!shouldRun) {
|
|
166
|
-
|
|
192
|
+
const skippedResult = {
|
|
167
193
|
status: "skipped" /* SKIPPED */,
|
|
168
194
|
duration: Date.now() - workStartTime
|
|
169
|
-
}
|
|
195
|
+
};
|
|
196
|
+
context.workResults.set(work.name, skippedResult);
|
|
197
|
+
workResults.set(work.name, skippedResult);
|
|
170
198
|
return { work, skipped: true };
|
|
171
199
|
}
|
|
172
200
|
}
|
|
@@ -186,19 +214,21 @@ var Workflow = class {
|
|
|
186
214
|
}
|
|
187
215
|
const duration = Date.now() - result.startTime;
|
|
188
216
|
if ("error" in result && result.error) {
|
|
189
|
-
|
|
217
|
+
const workResult = {
|
|
190
218
|
status: "failed" /* FAILED */,
|
|
191
219
|
error: result.error,
|
|
192
220
|
duration
|
|
193
|
-
}
|
|
221
|
+
};
|
|
222
|
+
workResults.set(result.work.name, workResult);
|
|
194
223
|
errors.push({ work: result.work, error: result.error });
|
|
195
224
|
} else {
|
|
196
|
-
|
|
197
|
-
workResults.set(result.work.name, {
|
|
225
|
+
const workResult = {
|
|
198
226
|
status: "completed" /* COMPLETED */,
|
|
199
227
|
result: result.result,
|
|
200
228
|
duration
|
|
201
|
-
}
|
|
229
|
+
};
|
|
230
|
+
context.workResults.set(result.work.name, workResult);
|
|
231
|
+
workResults.set(result.work.name, workResult);
|
|
202
232
|
}
|
|
203
233
|
}
|
|
204
234
|
if (errors.length > 0) {
|
|
@@ -213,8 +243,10 @@ var Workflow = class {
|
|
|
213
243
|
};
|
|
214
244
|
// Annotate the CommonJS export names for ESM import in node:
|
|
215
245
|
0 && (module.exports = {
|
|
246
|
+
Work,
|
|
216
247
|
WorkStatus,
|
|
217
248
|
Workflow,
|
|
218
|
-
WorkflowStatus
|
|
249
|
+
WorkflowStatus,
|
|
250
|
+
getWorkDefinition
|
|
219
251
|
});
|
|
220
252
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/workflow.types.ts","../src/workflow.ts"],"sourcesContent":["export * from './workflow';\nexport * from './workflow.types';\n","/**\n * Work Status\n */\nexport enum WorkStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n SKIPPED = 'skipped',\n}\n\n/**\n * Workflow Status\n */\nexport enum WorkflowStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n}\n\n/**\n * Context passed between workflow works\n * TData is the type of shared data between works\n * TWorkResults is a record mapping work names to their result types\n */\nexport interface IWorkflowContext<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Shared data between works */\n data: TData;\n /** Work-specific results keyed by work name with inferred types */\n workResults: IWorkResultsMap<TWorkResults>;\n}\n\n/**\n * Type-safe map for work results with automatic type inference\n */\nexport interface IWorkResultsMap<\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n get<K extends keyof TWorkResults>(name: K): TWorkResults[K] | undefined;\n set<K extends keyof TWorkResults>(name: K, value: TWorkResults[K]): void;\n has(name: keyof TWorkResults): boolean;\n}\n\n/**\n * Result of a single work execution\n */\nexport interface IWorkResult<TResult = unknown> {\n status: WorkStatus;\n result?: TResult;\n error?: Error;\n duration: number;\n}\n\n/**\n * Definition of a work with inferred name and result type\n */\nexport interface IWorkDefinition<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Unique name for the work */\n name: TName;\n /** Execute function - receives context and returns result */\n execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;\n /** Optional: condition to determine if work should run */\n shouldRun?: (\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => boolean | Promise<boolean>;\n /** Optional: called when work fails */\n onError?: (\n error: Error,\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => void | Promise<void>;\n}\n\n/**\n * Internal work representation\n */\nexport interface IWorkflowWork {\n type: 'serial' | 'parallel';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, any, any, any>[];\n}\n\n/**\n * Result of workflow execution\n */\nexport interface IWorkflowResult<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n status: WorkflowStatus;\n context: IWorkflowContext<TData, TWorkResults>;\n workResults: Map<keyof TWorkResults, IWorkResult>;\n totalDuration: number;\n error?: Error;\n}\n","import {\n IWorkflowContext,\n IWorkflowResult,\n IWorkResultsMap,\n WorkflowStatus,\n IWorkflowWork,\n IWorkDefinition,\n IWorkResult,\n WorkStatus,\n} from './workflow.types';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, unknown>();\n\n get<K extends keyof TWorkResults>(name: K): TWorkResults[K] | undefined {\n return this.map.get(name) as TWorkResults[K] | undefined;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: TWorkResults[K]): void {\n this.map.set(name, value);\n }\n\n has(name: keyof TWorkResults): boolean {\n return this.map.has(name);\n }\n}\n\n/**\n * A simple, extensible workflow engine that supports serial and parallel work execution.\n * Work names and result types are automatically inferred from the workflow definition.\n *\n * @example\n * ```typescript\n * const workflow = new Workflow<{ userId: string }>()\n * .serial({\n * name: 'validate',\n * execute: async (ctx) => true, // returns boolean\n * })\n * .parallel([\n * {\n * name: 'fetchOrders',\n * execute: async (ctx) => [{ id: 1 }], // returns Order[]\n * },\n * {\n * name: 'fetchProfile',\n * execute: async (ctx) => ({ name: 'John' }), // returns Profile\n * },\n * ])\n * .serial({\n * name: 'process',\n * execute: async (ctx) => {\n * // ✅ Autocomplete for names AND types are inferred!\n * const isValid = ctx.workResults.get('validate'); // boolean | undefined\n * const orders = ctx.workResults.get('fetchOrders'); // Order[] | undefined\n * const profile = ctx.workResults.get('fetchProfile'); // Profile | undefined\n * return { orders, profile };\n * },\n * });\n *\n * const result = await workflow.run({ userId: '123' });\n * ```\n */\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n\n /**\n * Add a serial work to the workflow.\n * The work name and result type are automatically inferred.\n */\n serial<TName extends string, TResult>(\n work: IWorkDefinition<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [work],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * All work names and result types are automatically inferred.\n */\n parallel<\n const TParallelWorks extends readonly IWorkDefinition<string, TData, unknown, TWorkResults>[],\n >(works: TParallelWorks): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n this.works.push({\n type: 'parallel',\n works: works as unknown as IWorkDefinition<string, TData, unknown, TWorkResults>[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n\n try {\n for (const workGroup of this.works) {\n if (workGroup.type === 'serial') {\n await this.executeWork(workGroup.works[0], context, workResults);\n } else {\n await this.executeParallelWorks(workGroup.works, context, workResults);\n }\n }\n\n return {\n status: WorkflowStatus.COMPLETED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n };\n } catch (error) {\n return {\n status: WorkflowStatus.FAILED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n /**\n * Execute a single work\n */\n private async executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n work: IWorkDefinition<string, TData, any, any>,\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n });\n return;\n }\n }\n\n try {\n const result = await work.execute(context);\n\n // Store result in context for subsequent works\n context.workResults.set(work.name as keyof TWorkResults, result);\n\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.COMPLETED,\n result,\n duration: Date.now() - workStartTime,\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.FAILED,\n error: err,\n duration: Date.now() - workStartTime,\n });\n\n // Call error handler if provided\n if (work.onError) {\n await work.onError(err, context);\n }\n\n // Re-throw to stop workflow execution\n throw err;\n }\n }\n\n /**\n * Execute multiple works in parallel\n */\n private async executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, TData, any, any>[],\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const promises = works.map(async (work) => {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n });\n return { work, skipped: true };\n }\n }\n\n try {\n const result = await work.execute(context);\n return { work, result, startTime: workStartTime };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return { work, error: err, startTime: workStartTime };\n }\n });\n\n const results = await Promise.all(promises);\n\n // Process results and check for errors\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const errors: { work: IWorkDefinition<string, TData, any, any>; error: Error }[] = [];\n\n for (const result of results) {\n if ('skipped' in result && result.skipped) {\n continue;\n }\n\n const duration = Date.now() - result.startTime!;\n\n if ('error' in result && result.error) {\n workResults.set(result.work.name as keyof TWorkResults, {\n status: WorkStatus.FAILED,\n error: result.error,\n duration,\n });\n errors.push({ work: result.work, error: result.error });\n } else {\n context.workResults.set(result.work.name as keyof TWorkResults, result.result);\n workResults.set(result.work.name as keyof TWorkResults, {\n status: WorkStatus.COMPLETED,\n result: result.result,\n duration,\n });\n }\n }\n\n // Handle errors after all parallel works complete\n if (errors.length > 0) {\n // Call error handlers\n for (const { work, error } of errors) {\n if (work.onError) {\n await work.onError(error, context);\n }\n }\n\n // Throw the first error to stop workflow\n throw errors[0].error;\n }\n }\n}\n\n/**\n * Helper type to extract work results from parallel works array\n * Uses Extract to preserve the specific type for each work name\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":"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","names":["WorkStatus","WorkflowStatus"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/workflow.types.ts","../src/work.ts","../src/workflow.ts"],"sourcesContent":["export * from './workflow';\nexport * from './workflow.types';\nexport * from './work';\n","/**\n * Work Status\n */\nexport enum WorkStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n SKIPPED = 'skipped',\n}\n\n/**\n * Workflow Status\n */\nexport enum WorkflowStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n}\n\n/**\n * Context passed between workflow works\n * TData is the type of shared data between works\n * TWorkResults is a record mapping work names to their result types\n */\nexport interface IWorkflowContext<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Shared data between works */\n data: TData;\n /** Work-specific results keyed by work name with inferred types */\n workResults: IWorkResultsMap<TWorkResults>;\n}\n\n/**\n * Type-safe map for work results with automatic type inference\n */\nexport interface IWorkResultsMap<\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]>;\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void;\n has(name: keyof TWorkResults): boolean;\n}\n\n/**\n * Result of a single work execution\n */\nexport interface IWorkResult<TResult = unknown> {\n status: WorkStatus;\n result?: TResult;\n error?: Error;\n duration: number;\n}\n\n/**\n * Definition of a work with inferred name and result type\n */\nexport interface IWorkDefinition<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Unique name for the work */\n name: TName;\n /** Execute function - receives context and returns result */\n execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;\n /** Optional: condition to determine if work should run */\n shouldRun?: (\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => boolean | Promise<boolean>;\n /** Optional: called when work fails */\n onError?: (\n error: Error,\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => void | Promise<void>;\n}\n\n/**\n * Internal work representation\n */\nexport interface IWorkflowWork {\n type: 'serial' | 'parallel';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, any, any, any>[];\n}\n\n/**\n * Result of workflow execution\n */\nexport interface IWorkflowResult<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n status: WorkflowStatus;\n context: IWorkflowContext<TData, TWorkResults>;\n workResults: Map<keyof TWorkResults, IWorkResult>;\n totalDuration: number;\n error?: Error;\n}\n","import { IWorkDefinition, IWorkflowContext } from './workflow.types';\n\n/**\n * A standalone Work unit that can be added to workflows.\n * Implements IWorkDefinition so it can be used anywhere a work definition is expected.\n *\n * @example\n * ```typescript\n * const fetchUser = new Work({\n * name: 'fetchUser',\n * execute: async (ctx) => {\n * return { id: ctx.data.userId, name: 'John' };\n * },\n * });\n *\n * const workflow = new Workflow<{ userId: string }>()\n * .serial(fetchUser)\n * .parallel([work1, work2]);\n * ```\n */\nexport class Work<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> implements IWorkDefinition<TName, TData, TResult, TAvailableWorkResults> {\n /** Unique name for the work */\n readonly name: TName;\n\n /** Execute function - receives context and returns result */\n readonly execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;\n\n /** Optional: condition to determine if work should run */\n readonly shouldRun?: (\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => boolean | Promise<boolean>;\n\n /** Optional: called when work fails */\n readonly onError?: (\n error: Error,\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => void | Promise<void>;\n\n constructor(definition: IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>) {\n this.name = definition.name;\n this.execute = definition.execute;\n this.shouldRun = definition.shouldRun;\n this.onError = definition.onError;\n }\n}\n\n/**\n * Type that accepts a work definition (either inline object or Work instance).\n * Since Work implements IWorkDefinition, this is simply IWorkDefinition.\n */\nexport type WorkInput<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> = IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>;\n\n/**\n * Helper to get the work definition from a WorkInput.\n * Since Work implements IWorkDefinition, this simply returns the input.\n */\nexport function getWorkDefinition<\n TName extends string,\n TData,\n TResult,\n TAvailableWorkResults extends Record<string, unknown>,\n>(\n input: WorkInput<TName, TData, TResult, TAvailableWorkResults>\n): IWorkDefinition<TName, TData, TResult, TAvailableWorkResults> {\n return input;\n}\n","import {\n IWorkflowContext,\n IWorkflowResult,\n IWorkResultsMap,\n WorkflowStatus,\n IWorkflowWork,\n IWorkDefinition,\n IWorkResult,\n WorkStatus,\n} from './workflow.types';\nimport { WorkInput, getWorkDefinition } from './work';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, IWorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]> {\n const result = this.map.get(name);\n if (!result) {\n throw new Error(\n `Work result \"${String(name)}\" not found. This work may not have executed yet.`\n );\n }\n return result as IWorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has(name: keyof TWorkResults): boolean {\n return this.map.has(name);\n }\n}\n\n/**\n * A simple, extensible workflow engine that supports serial and parallel work execution.\n * Work names and result types are automatically inferred from the workflow definition.\n *\n * @example\n * ```typescript\n * const workflow = new Workflow<{ userId: string }>()\n * .serial({\n * name: 'validate',\n * execute: async (ctx) => true, // returns boolean\n * })\n * .parallel([\n * {\n * name: 'fetchOrders',\n * execute: async (ctx) => [{ id: 1 }], // returns Order[]\n * },\n * {\n * name: 'fetchProfile',\n * execute: async (ctx) => ({ name: 'John' }), // returns Profile\n * },\n * ])\n * .serial({\n * name: 'process',\n * execute: async (ctx) => {\n * // ✅ Autocomplete for names AND types are inferred!\n * const isValid = ctx.workResults.get('validate').result; // boolean | undefined\n * const orders = ctx.workResults.get('fetchOrders').result; // Order[] | undefined\n * const profile = ctx.workResults.get('fetchProfile').result; // Profile | undefined\n * return { orders, profile };\n * },\n * });\n *\n * const result = await workflow.run({ userId: '123' });\n * ```\n */\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n\n /**\n * Add a serial work to the workflow.\n * Accepts either an inline work definition or a Work instance.\n * The work name and result type are automatically inferred.\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts either inline work definitions or Work instances.\n * All work names and result types are automatically inferred.\n */\n parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(\n works: TParallelWorks\n ): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)) as unknown as IWorkDefinition<\n string,\n TData,\n unknown,\n TWorkResults\n >[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n\n try {\n for (const workGroup of this.works) {\n if (workGroup.type === 'serial') {\n await this.executeWork(workGroup.works[0], context, workResults);\n } else {\n await this.executeParallelWorks(workGroup.works, context, workResults);\n }\n }\n\n return {\n status: WorkflowStatus.COMPLETED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n };\n } catch (error) {\n return {\n status: WorkflowStatus.FAILED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n /**\n * Execute a single work\n */\n private async executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n work: IWorkDefinition<string, TData, any, any>,\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n const skippedResult: IWorkResult = {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, skippedResult as any);\n workResults.set(work.name as keyof TWorkResults, skippedResult);\n return;\n }\n }\n\n try {\n const result = await work.execute(context);\n\n const workResult: IWorkResult = {\n status: WorkStatus.COMPLETED,\n result,\n duration: Date.now() - workStartTime,\n };\n\n // Store result in context for subsequent works\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, workResult as any);\n workResults.set(work.name as keyof TWorkResults, workResult);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.FAILED,\n error: err,\n duration: Date.now() - workStartTime,\n });\n\n // Call error handler if provided\n if (work.onError) {\n await work.onError(err, context);\n }\n\n // Re-throw to stop workflow execution\n throw err;\n }\n }\n\n /**\n * Execute multiple works in parallel\n */\n private async executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, TData, any, any>[],\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const promises = works.map(async (work) => {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n const skippedResult: IWorkResult = {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, skippedResult as any);\n workResults.set(work.name as keyof TWorkResults, skippedResult);\n return { work, skipped: true };\n }\n }\n\n try {\n const result = await work.execute(context);\n return { work, result, startTime: workStartTime };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return { work, error: err, startTime: workStartTime };\n }\n });\n\n const results = await Promise.all(promises);\n\n // Process results and check for errors\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const errors: { work: IWorkDefinition<string, TData, any, any>; error: Error }[] = [];\n\n for (const result of results) {\n if ('skipped' in result && result.skipped) {\n continue;\n }\n\n const duration = Date.now() - result.startTime!;\n\n if ('error' in result && result.error) {\n const workResult: IWorkResult = {\n status: WorkStatus.FAILED,\n error: result.error,\n duration,\n };\n workResults.set(result.work.name as keyof TWorkResults, workResult);\n errors.push({ work: result.work, error: result.error });\n } else {\n const workResult: IWorkResult = {\n status: WorkStatus.COMPLETED,\n result: result.result,\n duration,\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(result.work.name as keyof TWorkResults, workResult as any);\n workResults.set(result.work.name as keyof TWorkResults, workResult);\n }\n }\n\n // Handle errors after all parallel works complete\n if (errors.length > 0) {\n // Call error handlers\n for (const { work, error } of errors) {\n if (work.onError) {\n await work.onError(error, context);\n }\n }\n\n // Throw the first error to stop workflow\n throw errors[0].error;\n }\n }\n}\n\n/**\n * Helper type to extract work results from parallel works array.\n * Since Work implements IWorkDefinition, we can use Extract directly.\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":"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","names":["WorkStatus","WorkflowStatus"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -32,8 +32,8 @@ interface IWorkflowContext<TData = Record<string, unknown>, TWorkResults extends
|
|
|
32
32
|
* Type-safe map for work results with automatic type inference
|
|
33
33
|
*/
|
|
34
34
|
interface IWorkResultsMap<TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
|
|
35
|
-
get<K extends keyof TWorkResults>(name: K): TWorkResults[K]
|
|
36
|
-
set<K extends keyof TWorkResults>(name: K, value: TWorkResults[K]): void;
|
|
35
|
+
get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]>;
|
|
36
|
+
set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void;
|
|
37
37
|
has(name: keyof TWorkResults): boolean;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
@@ -76,6 +76,46 @@ interface IWorkflowResult<TData = Record<string, unknown>, TWorkResults extends
|
|
|
76
76
|
error?: Error;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* A standalone Work unit that can be added to workflows.
|
|
81
|
+
* Implements IWorkDefinition so it can be used anywhere a work definition is expected.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const fetchUser = new Work({
|
|
86
|
+
* name: 'fetchUser',
|
|
87
|
+
* execute: async (ctx) => {
|
|
88
|
+
* return { id: ctx.data.userId, name: 'John' };
|
|
89
|
+
* },
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* const workflow = new Workflow<{ userId: string }>()
|
|
93
|
+
* .serial(fetchUser)
|
|
94
|
+
* .parallel([work1, work2]);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare class Work<TName extends string, TData = Record<string, unknown>, TResult = unknown, TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>> implements IWorkDefinition<TName, TData, TResult, TAvailableWorkResults> {
|
|
98
|
+
/** Unique name for the work */
|
|
99
|
+
readonly name: TName;
|
|
100
|
+
/** Execute function - receives context and returns result */
|
|
101
|
+
readonly execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;
|
|
102
|
+
/** Optional: condition to determine if work should run */
|
|
103
|
+
readonly shouldRun?: (context: IWorkflowContext<TData, TAvailableWorkResults>) => boolean | Promise<boolean>;
|
|
104
|
+
/** Optional: called when work fails */
|
|
105
|
+
readonly onError?: (error: Error, context: IWorkflowContext<TData, TAvailableWorkResults>) => void | Promise<void>;
|
|
106
|
+
constructor(definition: IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Type that accepts a work definition (either inline object or Work instance).
|
|
110
|
+
* Since Work implements IWorkDefinition, this is simply IWorkDefinition.
|
|
111
|
+
*/
|
|
112
|
+
type WorkInput<TName extends string, TData = Record<string, unknown>, TResult = unknown, TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>> = IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>;
|
|
113
|
+
/**
|
|
114
|
+
* Helper to get the work definition from a WorkInput.
|
|
115
|
+
* Since Work implements IWorkDefinition, this simply returns the input.
|
|
116
|
+
*/
|
|
117
|
+
declare function getWorkDefinition<TName extends string, TData, TResult, TAvailableWorkResults extends Record<string, unknown>>(input: WorkInput<TName, TData, TResult, TAvailableWorkResults>): IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>;
|
|
118
|
+
|
|
79
119
|
/**
|
|
80
120
|
* A simple, extensible workflow engine that supports serial and parallel work execution.
|
|
81
121
|
* Work names and result types are automatically inferred from the workflow definition.
|
|
@@ -101,9 +141,9 @@ interface IWorkflowResult<TData = Record<string, unknown>, TWorkResults extends
|
|
|
101
141
|
* name: 'process',
|
|
102
142
|
* execute: async (ctx) => {
|
|
103
143
|
* // ✅ Autocomplete for names AND types are inferred!
|
|
104
|
-
* const isValid = ctx.workResults.get('validate'); // boolean | undefined
|
|
105
|
-
* const orders = ctx.workResults.get('fetchOrders'); // Order[] | undefined
|
|
106
|
-
* const profile = ctx.workResults.get('fetchProfile'); // Profile | undefined
|
|
144
|
+
* const isValid = ctx.workResults.get('validate').result; // boolean | undefined
|
|
145
|
+
* const orders = ctx.workResults.get('fetchOrders').result; // Order[] | undefined
|
|
146
|
+
* const profile = ctx.workResults.get('fetchProfile').result; // Profile | undefined
|
|
107
147
|
* return { orders, profile };
|
|
108
148
|
* },
|
|
109
149
|
* });
|
|
@@ -115,16 +155,18 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
115
155
|
private works;
|
|
116
156
|
/**
|
|
117
157
|
* Add a serial work to the workflow.
|
|
158
|
+
* Accepts either an inline work definition or a Work instance.
|
|
118
159
|
* The work name and result type are automatically inferred.
|
|
119
160
|
*/
|
|
120
|
-
serial<TName extends string, TResult>(work:
|
|
161
|
+
serial<TName extends string, TResult>(work: WorkInput<TName, TData, TResult, TWorkResults>): Workflow<TData, TWorkResults & {
|
|
121
162
|
[K in TName]: TResult;
|
|
122
163
|
}>;
|
|
123
164
|
/**
|
|
124
165
|
* Add parallel works to the workflow.
|
|
166
|
+
* Accepts either inline work definitions or Work instances.
|
|
125
167
|
* All work names and result types are automatically inferred.
|
|
126
168
|
*/
|
|
127
|
-
parallel<const TParallelWorks extends readonly
|
|
169
|
+
parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(works: TParallelWorks): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;
|
|
128
170
|
/**
|
|
129
171
|
* Execute the workflow with initial data
|
|
130
172
|
*/
|
|
@@ -139,8 +181,8 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
139
181
|
private executeParallelWorks;
|
|
140
182
|
}
|
|
141
183
|
/**
|
|
142
|
-
* Helper type to extract work results from parallel works array
|
|
143
|
-
*
|
|
184
|
+
* Helper type to extract work results from parallel works array.
|
|
185
|
+
* Since Work implements IWorkDefinition, we can use Extract directly.
|
|
144
186
|
*/
|
|
145
187
|
type ParallelWorksToRecord<T extends readonly IWorkDefinition<string, any, any, any>[]> = {
|
|
146
188
|
[K in T[number]['name']]: Extract<T[number], {
|
|
@@ -148,4 +190,4 @@ type ParallelWorksToRecord<T extends readonly IWorkDefinition<string, any, any,
|
|
|
148
190
|
}> extends IWorkDefinition<string, any, infer R, any> ? R : never;
|
|
149
191
|
};
|
|
150
192
|
|
|
151
|
-
export { type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, WorkStatus, Workflow, WorkflowStatus };
|
|
193
|
+
export { type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, Work, type WorkInput, WorkStatus, Workflow, WorkflowStatus, getWorkDefinition };
|
package/dist/index.d.ts
CHANGED
|
@@ -32,8 +32,8 @@ interface IWorkflowContext<TData = Record<string, unknown>, TWorkResults extends
|
|
|
32
32
|
* Type-safe map for work results with automatic type inference
|
|
33
33
|
*/
|
|
34
34
|
interface IWorkResultsMap<TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
|
|
35
|
-
get<K extends keyof TWorkResults>(name: K): TWorkResults[K]
|
|
36
|
-
set<K extends keyof TWorkResults>(name: K, value: TWorkResults[K]): void;
|
|
35
|
+
get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]>;
|
|
36
|
+
set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void;
|
|
37
37
|
has(name: keyof TWorkResults): boolean;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
@@ -76,6 +76,46 @@ interface IWorkflowResult<TData = Record<string, unknown>, TWorkResults extends
|
|
|
76
76
|
error?: Error;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* A standalone Work unit that can be added to workflows.
|
|
81
|
+
* Implements IWorkDefinition so it can be used anywhere a work definition is expected.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const fetchUser = new Work({
|
|
86
|
+
* name: 'fetchUser',
|
|
87
|
+
* execute: async (ctx) => {
|
|
88
|
+
* return { id: ctx.data.userId, name: 'John' };
|
|
89
|
+
* },
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* const workflow = new Workflow<{ userId: string }>()
|
|
93
|
+
* .serial(fetchUser)
|
|
94
|
+
* .parallel([work1, work2]);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare class Work<TName extends string, TData = Record<string, unknown>, TResult = unknown, TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>> implements IWorkDefinition<TName, TData, TResult, TAvailableWorkResults> {
|
|
98
|
+
/** Unique name for the work */
|
|
99
|
+
readonly name: TName;
|
|
100
|
+
/** Execute function - receives context and returns result */
|
|
101
|
+
readonly execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;
|
|
102
|
+
/** Optional: condition to determine if work should run */
|
|
103
|
+
readonly shouldRun?: (context: IWorkflowContext<TData, TAvailableWorkResults>) => boolean | Promise<boolean>;
|
|
104
|
+
/** Optional: called when work fails */
|
|
105
|
+
readonly onError?: (error: Error, context: IWorkflowContext<TData, TAvailableWorkResults>) => void | Promise<void>;
|
|
106
|
+
constructor(definition: IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Type that accepts a work definition (either inline object or Work instance).
|
|
110
|
+
* Since Work implements IWorkDefinition, this is simply IWorkDefinition.
|
|
111
|
+
*/
|
|
112
|
+
type WorkInput<TName extends string, TData = Record<string, unknown>, TResult = unknown, TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>> = IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>;
|
|
113
|
+
/**
|
|
114
|
+
* Helper to get the work definition from a WorkInput.
|
|
115
|
+
* Since Work implements IWorkDefinition, this simply returns the input.
|
|
116
|
+
*/
|
|
117
|
+
declare function getWorkDefinition<TName extends string, TData, TResult, TAvailableWorkResults extends Record<string, unknown>>(input: WorkInput<TName, TData, TResult, TAvailableWorkResults>): IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>;
|
|
118
|
+
|
|
79
119
|
/**
|
|
80
120
|
* A simple, extensible workflow engine that supports serial and parallel work execution.
|
|
81
121
|
* Work names and result types are automatically inferred from the workflow definition.
|
|
@@ -101,9 +141,9 @@ interface IWorkflowResult<TData = Record<string, unknown>, TWorkResults extends
|
|
|
101
141
|
* name: 'process',
|
|
102
142
|
* execute: async (ctx) => {
|
|
103
143
|
* // ✅ Autocomplete for names AND types are inferred!
|
|
104
|
-
* const isValid = ctx.workResults.get('validate'); // boolean | undefined
|
|
105
|
-
* const orders = ctx.workResults.get('fetchOrders'); // Order[] | undefined
|
|
106
|
-
* const profile = ctx.workResults.get('fetchProfile'); // Profile | undefined
|
|
144
|
+
* const isValid = ctx.workResults.get('validate').result; // boolean | undefined
|
|
145
|
+
* const orders = ctx.workResults.get('fetchOrders').result; // Order[] | undefined
|
|
146
|
+
* const profile = ctx.workResults.get('fetchProfile').result; // Profile | undefined
|
|
107
147
|
* return { orders, profile };
|
|
108
148
|
* },
|
|
109
149
|
* });
|
|
@@ -115,16 +155,18 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
115
155
|
private works;
|
|
116
156
|
/**
|
|
117
157
|
* Add a serial work to the workflow.
|
|
158
|
+
* Accepts either an inline work definition or a Work instance.
|
|
118
159
|
* The work name and result type are automatically inferred.
|
|
119
160
|
*/
|
|
120
|
-
serial<TName extends string, TResult>(work:
|
|
161
|
+
serial<TName extends string, TResult>(work: WorkInput<TName, TData, TResult, TWorkResults>): Workflow<TData, TWorkResults & {
|
|
121
162
|
[K in TName]: TResult;
|
|
122
163
|
}>;
|
|
123
164
|
/**
|
|
124
165
|
* Add parallel works to the workflow.
|
|
166
|
+
* Accepts either inline work definitions or Work instances.
|
|
125
167
|
* All work names and result types are automatically inferred.
|
|
126
168
|
*/
|
|
127
|
-
parallel<const TParallelWorks extends readonly
|
|
169
|
+
parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(works: TParallelWorks): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;
|
|
128
170
|
/**
|
|
129
171
|
* Execute the workflow with initial data
|
|
130
172
|
*/
|
|
@@ -139,8 +181,8 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
139
181
|
private executeParallelWorks;
|
|
140
182
|
}
|
|
141
183
|
/**
|
|
142
|
-
* Helper type to extract work results from parallel works array
|
|
143
|
-
*
|
|
184
|
+
* Helper type to extract work results from parallel works array.
|
|
185
|
+
* Since Work implements IWorkDefinition, we can use Extract directly.
|
|
144
186
|
*/
|
|
145
187
|
type ParallelWorksToRecord<T extends readonly IWorkDefinition<string, any, any, any>[]> = {
|
|
146
188
|
[K in T[number]['name']]: Extract<T[number], {
|
|
@@ -148,4 +190,4 @@ type ParallelWorksToRecord<T extends readonly IWorkDefinition<string, any, any,
|
|
|
148
190
|
}> extends IWorkDefinition<string, any, infer R, any> ? R : never;
|
|
149
191
|
};
|
|
150
192
|
|
|
151
|
-
export { type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, WorkStatus, Workflow, WorkflowStatus };
|
|
193
|
+
export { type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, Work, type WorkInput, WorkStatus, Workflow, WorkflowStatus, getWorkDefinition };
|
package/dist/index.js
CHANGED
|
@@ -15,13 +15,32 @@ var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
|
|
|
15
15
|
return WorkflowStatus2;
|
|
16
16
|
})(WorkflowStatus || {});
|
|
17
17
|
|
|
18
|
+
// src/work.ts
|
|
19
|
+
var Work = class {
|
|
20
|
+
constructor(definition) {
|
|
21
|
+
this.name = definition.name;
|
|
22
|
+
this.execute = definition.execute;
|
|
23
|
+
this.shouldRun = definition.shouldRun;
|
|
24
|
+
this.onError = definition.onError;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
function getWorkDefinition(input) {
|
|
28
|
+
return input;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
// src/workflow.ts
|
|
19
32
|
var WorkResultsMap = class {
|
|
20
33
|
constructor() {
|
|
21
34
|
this.map = /* @__PURE__ */ new Map();
|
|
22
35
|
}
|
|
23
36
|
get(name) {
|
|
24
|
-
|
|
37
|
+
const result = this.map.get(name);
|
|
38
|
+
if (!result) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Work result "${String(name)}" not found. This work may not have executed yet.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
25
44
|
}
|
|
26
45
|
set(name, value) {
|
|
27
46
|
this.map.set(name, value);
|
|
@@ -36,23 +55,25 @@ var Workflow = class {
|
|
|
36
55
|
}
|
|
37
56
|
/**
|
|
38
57
|
* Add a serial work to the workflow.
|
|
58
|
+
* Accepts either an inline work definition or a Work instance.
|
|
39
59
|
* The work name and result type are automatically inferred.
|
|
40
60
|
*/
|
|
41
61
|
serial(work) {
|
|
42
62
|
this.works.push({
|
|
43
63
|
type: "serial",
|
|
44
|
-
works: [work]
|
|
64
|
+
works: [getWorkDefinition(work)]
|
|
45
65
|
});
|
|
46
66
|
return this;
|
|
47
67
|
}
|
|
48
68
|
/**
|
|
49
69
|
* Add parallel works to the workflow.
|
|
70
|
+
* Accepts either inline work definitions or Work instances.
|
|
50
71
|
* All work names and result types are automatically inferred.
|
|
51
72
|
*/
|
|
52
73
|
parallel(works) {
|
|
53
74
|
this.works.push({
|
|
54
75
|
type: "parallel",
|
|
55
|
-
works
|
|
76
|
+
works: works.map((w) => getWorkDefinition(w))
|
|
56
77
|
});
|
|
57
78
|
return this;
|
|
58
79
|
}
|
|
@@ -98,21 +119,24 @@ var Workflow = class {
|
|
|
98
119
|
if (work.shouldRun) {
|
|
99
120
|
const shouldRun = await work.shouldRun(context);
|
|
100
121
|
if (!shouldRun) {
|
|
101
|
-
|
|
122
|
+
const skippedResult = {
|
|
102
123
|
status: "skipped" /* SKIPPED */,
|
|
103
124
|
duration: Date.now() - workStartTime
|
|
104
|
-
}
|
|
125
|
+
};
|
|
126
|
+
context.workResults.set(work.name, skippedResult);
|
|
127
|
+
workResults.set(work.name, skippedResult);
|
|
105
128
|
return;
|
|
106
129
|
}
|
|
107
130
|
}
|
|
108
131
|
try {
|
|
109
132
|
const result = await work.execute(context);
|
|
110
|
-
|
|
111
|
-
workResults.set(work.name, {
|
|
133
|
+
const workResult = {
|
|
112
134
|
status: "completed" /* COMPLETED */,
|
|
113
135
|
result,
|
|
114
136
|
duration: Date.now() - workStartTime
|
|
115
|
-
}
|
|
137
|
+
};
|
|
138
|
+
context.workResults.set(work.name, workResult);
|
|
139
|
+
workResults.set(work.name, workResult);
|
|
116
140
|
} catch (error) {
|
|
117
141
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
118
142
|
workResults.set(work.name, {
|
|
@@ -135,10 +159,12 @@ var Workflow = class {
|
|
|
135
159
|
if (work.shouldRun) {
|
|
136
160
|
const shouldRun = await work.shouldRun(context);
|
|
137
161
|
if (!shouldRun) {
|
|
138
|
-
|
|
162
|
+
const skippedResult = {
|
|
139
163
|
status: "skipped" /* SKIPPED */,
|
|
140
164
|
duration: Date.now() - workStartTime
|
|
141
|
-
}
|
|
165
|
+
};
|
|
166
|
+
context.workResults.set(work.name, skippedResult);
|
|
167
|
+
workResults.set(work.name, skippedResult);
|
|
142
168
|
return { work, skipped: true };
|
|
143
169
|
}
|
|
144
170
|
}
|
|
@@ -158,19 +184,21 @@ var Workflow = class {
|
|
|
158
184
|
}
|
|
159
185
|
const duration = Date.now() - result.startTime;
|
|
160
186
|
if ("error" in result && result.error) {
|
|
161
|
-
|
|
187
|
+
const workResult = {
|
|
162
188
|
status: "failed" /* FAILED */,
|
|
163
189
|
error: result.error,
|
|
164
190
|
duration
|
|
165
|
-
}
|
|
191
|
+
};
|
|
192
|
+
workResults.set(result.work.name, workResult);
|
|
166
193
|
errors.push({ work: result.work, error: result.error });
|
|
167
194
|
} else {
|
|
168
|
-
|
|
169
|
-
workResults.set(result.work.name, {
|
|
195
|
+
const workResult = {
|
|
170
196
|
status: "completed" /* COMPLETED */,
|
|
171
197
|
result: result.result,
|
|
172
198
|
duration
|
|
173
|
-
}
|
|
199
|
+
};
|
|
200
|
+
context.workResults.set(result.work.name, workResult);
|
|
201
|
+
workResults.set(result.work.name, workResult);
|
|
174
202
|
}
|
|
175
203
|
}
|
|
176
204
|
if (errors.length > 0) {
|
|
@@ -184,8 +212,10 @@ var Workflow = class {
|
|
|
184
212
|
}
|
|
185
213
|
};
|
|
186
214
|
export {
|
|
215
|
+
Work,
|
|
187
216
|
WorkStatus,
|
|
188
217
|
Workflow,
|
|
189
|
-
WorkflowStatus
|
|
218
|
+
WorkflowStatus,
|
|
219
|
+
getWorkDefinition
|
|
190
220
|
};
|
|
191
221
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workflow.types.ts","../src/workflow.ts"],"sourcesContent":["/**\n * Work Status\n */\nexport enum WorkStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n SKIPPED = 'skipped',\n}\n\n/**\n * Workflow Status\n */\nexport enum WorkflowStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n}\n\n/**\n * Context passed between workflow works\n * TData is the type of shared data between works\n * TWorkResults is a record mapping work names to their result types\n */\nexport interface IWorkflowContext<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Shared data between works */\n data: TData;\n /** Work-specific results keyed by work name with inferred types */\n workResults: IWorkResultsMap<TWorkResults>;\n}\n\n/**\n * Type-safe map for work results with automatic type inference\n */\nexport interface IWorkResultsMap<\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n get<K extends keyof TWorkResults>(name: K): TWorkResults[K] | undefined;\n set<K extends keyof TWorkResults>(name: K, value: TWorkResults[K]): void;\n has(name: keyof TWorkResults): boolean;\n}\n\n/**\n * Result of a single work execution\n */\nexport interface IWorkResult<TResult = unknown> {\n status: WorkStatus;\n result?: TResult;\n error?: Error;\n duration: number;\n}\n\n/**\n * Definition of a work with inferred name and result type\n */\nexport interface IWorkDefinition<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Unique name for the work */\n name: TName;\n /** Execute function - receives context and returns result */\n execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;\n /** Optional: condition to determine if work should run */\n shouldRun?: (\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => boolean | Promise<boolean>;\n /** Optional: called when work fails */\n onError?: (\n error: Error,\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => void | Promise<void>;\n}\n\n/**\n * Internal work representation\n */\nexport interface IWorkflowWork {\n type: 'serial' | 'parallel';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, any, any, any>[];\n}\n\n/**\n * Result of workflow execution\n */\nexport interface IWorkflowResult<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n status: WorkflowStatus;\n context: IWorkflowContext<TData, TWorkResults>;\n workResults: Map<keyof TWorkResults, IWorkResult>;\n totalDuration: number;\n error?: Error;\n}\n","import {\n IWorkflowContext,\n IWorkflowResult,\n IWorkResultsMap,\n WorkflowStatus,\n IWorkflowWork,\n IWorkDefinition,\n IWorkResult,\n WorkStatus,\n} from './workflow.types';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, unknown>();\n\n get<K extends keyof TWorkResults>(name: K): TWorkResults[K] | undefined {\n return this.map.get(name) as TWorkResults[K] | undefined;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: TWorkResults[K]): void {\n this.map.set(name, value);\n }\n\n has(name: keyof TWorkResults): boolean {\n return this.map.has(name);\n }\n}\n\n/**\n * A simple, extensible workflow engine that supports serial and parallel work execution.\n * Work names and result types are automatically inferred from the workflow definition.\n *\n * @example\n * ```typescript\n * const workflow = new Workflow<{ userId: string }>()\n * .serial({\n * name: 'validate',\n * execute: async (ctx) => true, // returns boolean\n * })\n * .parallel([\n * {\n * name: 'fetchOrders',\n * execute: async (ctx) => [{ id: 1 }], // returns Order[]\n * },\n * {\n * name: 'fetchProfile',\n * execute: async (ctx) => ({ name: 'John' }), // returns Profile\n * },\n * ])\n * .serial({\n * name: 'process',\n * execute: async (ctx) => {\n * // ✅ Autocomplete for names AND types are inferred!\n * const isValid = ctx.workResults.get('validate'); // boolean | undefined\n * const orders = ctx.workResults.get('fetchOrders'); // Order[] | undefined\n * const profile = ctx.workResults.get('fetchProfile'); // Profile | undefined\n * return { orders, profile };\n * },\n * });\n *\n * const result = await workflow.run({ userId: '123' });\n * ```\n */\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n\n /**\n * Add a serial work to the workflow.\n * The work name and result type are automatically inferred.\n */\n serial<TName extends string, TResult>(\n work: IWorkDefinition<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [work],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * All work names and result types are automatically inferred.\n */\n parallel<\n const TParallelWorks extends readonly IWorkDefinition<string, TData, unknown, TWorkResults>[],\n >(works: TParallelWorks): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n this.works.push({\n type: 'parallel',\n works: works as unknown as IWorkDefinition<string, TData, unknown, TWorkResults>[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n\n try {\n for (const workGroup of this.works) {\n if (workGroup.type === 'serial') {\n await this.executeWork(workGroup.works[0], context, workResults);\n } else {\n await this.executeParallelWorks(workGroup.works, context, workResults);\n }\n }\n\n return {\n status: WorkflowStatus.COMPLETED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n };\n } catch (error) {\n return {\n status: WorkflowStatus.FAILED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n /**\n * Execute a single work\n */\n private async executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n work: IWorkDefinition<string, TData, any, any>,\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n });\n return;\n }\n }\n\n try {\n const result = await work.execute(context);\n\n // Store result in context for subsequent works\n context.workResults.set(work.name as keyof TWorkResults, result);\n\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.COMPLETED,\n result,\n duration: Date.now() - workStartTime,\n });\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.FAILED,\n error: err,\n duration: Date.now() - workStartTime,\n });\n\n // Call error handler if provided\n if (work.onError) {\n await work.onError(err, context);\n }\n\n // Re-throw to stop workflow execution\n throw err;\n }\n }\n\n /**\n * Execute multiple works in parallel\n */\n private async executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, TData, any, any>[],\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const promises = works.map(async (work) => {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n });\n return { work, skipped: true };\n }\n }\n\n try {\n const result = await work.execute(context);\n return { work, result, startTime: workStartTime };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return { work, error: err, startTime: workStartTime };\n }\n });\n\n const results = await Promise.all(promises);\n\n // Process results and check for errors\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const errors: { work: IWorkDefinition<string, TData, any, any>; error: Error }[] = [];\n\n for (const result of results) {\n if ('skipped' in result && result.skipped) {\n continue;\n }\n\n const duration = Date.now() - result.startTime!;\n\n if ('error' in result && result.error) {\n workResults.set(result.work.name as keyof TWorkResults, {\n status: WorkStatus.FAILED,\n error: result.error,\n duration,\n });\n errors.push({ work: result.work, error: result.error });\n } else {\n context.workResults.set(result.work.name as keyof TWorkResults, result.result);\n workResults.set(result.work.name as keyof TWorkResults, {\n status: WorkStatus.COMPLETED,\n result: result.result,\n duration,\n });\n }\n }\n\n // Handle errors after all parallel works complete\n if (errors.length > 0) {\n // Call error handlers\n for (const { work, error } of errors) {\n if (work.onError) {\n await work.onError(error, context);\n }\n }\n\n // Throw the first error to stop workflow\n throw errors[0].error;\n }\n }\n}\n\n/**\n * Helper type to extract work results from parallel works array\n * Uses Extract to preserve the specific type for each work name\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":"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","names":["WorkStatus","WorkflowStatus"]}
|
|
1
|
+
{"version":3,"sources":["../src/workflow.types.ts","../src/work.ts","../src/workflow.ts"],"sourcesContent":["/**\n * Work Status\n */\nexport enum WorkStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n SKIPPED = 'skipped',\n}\n\n/**\n * Workflow Status\n */\nexport enum WorkflowStatus {\n PENDING = 'pending',\n RUNNING = 'running',\n COMPLETED = 'completed',\n FAILED = 'failed',\n}\n\n/**\n * Context passed between workflow works\n * TData is the type of shared data between works\n * TWorkResults is a record mapping work names to their result types\n */\nexport interface IWorkflowContext<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Shared data between works */\n data: TData;\n /** Work-specific results keyed by work name with inferred types */\n workResults: IWorkResultsMap<TWorkResults>;\n}\n\n/**\n * Type-safe map for work results with automatic type inference\n */\nexport interface IWorkResultsMap<\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]>;\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void;\n has(name: keyof TWorkResults): boolean;\n}\n\n/**\n * Result of a single work execution\n */\nexport interface IWorkResult<TResult = unknown> {\n status: WorkStatus;\n result?: TResult;\n error?: Error;\n duration: number;\n}\n\n/**\n * Definition of a work with inferred name and result type\n */\nexport interface IWorkDefinition<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Unique name for the work */\n name: TName;\n /** Execute function - receives context and returns result */\n execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;\n /** Optional: condition to determine if work should run */\n shouldRun?: (\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => boolean | Promise<boolean>;\n /** Optional: called when work fails */\n onError?: (\n error: Error,\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => void | Promise<void>;\n}\n\n/**\n * Internal work representation\n */\nexport interface IWorkflowWork {\n type: 'serial' | 'parallel';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, any, any, any>[];\n}\n\n/**\n * Result of workflow execution\n */\nexport interface IWorkflowResult<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n status: WorkflowStatus;\n context: IWorkflowContext<TData, TWorkResults>;\n workResults: Map<keyof TWorkResults, IWorkResult>;\n totalDuration: number;\n error?: Error;\n}\n","import { IWorkDefinition, IWorkflowContext } from './workflow.types';\n\n/**\n * A standalone Work unit that can be added to workflows.\n * Implements IWorkDefinition so it can be used anywhere a work definition is expected.\n *\n * @example\n * ```typescript\n * const fetchUser = new Work({\n * name: 'fetchUser',\n * execute: async (ctx) => {\n * return { id: ctx.data.userId, name: 'John' };\n * },\n * });\n *\n * const workflow = new Workflow<{ userId: string }>()\n * .serial(fetchUser)\n * .parallel([work1, work2]);\n * ```\n */\nexport class Work<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> implements IWorkDefinition<TName, TData, TResult, TAvailableWorkResults> {\n /** Unique name for the work */\n readonly name: TName;\n\n /** Execute function - receives context and returns result */\n readonly execute: (context: IWorkflowContext<TData, TAvailableWorkResults>) => Promise<TResult>;\n\n /** Optional: condition to determine if work should run */\n readonly shouldRun?: (\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => boolean | Promise<boolean>;\n\n /** Optional: called when work fails */\n readonly onError?: (\n error: Error,\n context: IWorkflowContext<TData, TAvailableWorkResults>\n ) => void | Promise<void>;\n\n constructor(definition: IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>) {\n this.name = definition.name;\n this.execute = definition.execute;\n this.shouldRun = definition.shouldRun;\n this.onError = definition.onError;\n }\n}\n\n/**\n * Type that accepts a work definition (either inline object or Work instance).\n * Since Work implements IWorkDefinition, this is simply IWorkDefinition.\n */\nexport type WorkInput<\n TName extends string,\n TData = Record<string, unknown>,\n TResult = unknown,\n TAvailableWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> = IWorkDefinition<TName, TData, TResult, TAvailableWorkResults>;\n\n/**\n * Helper to get the work definition from a WorkInput.\n * Since Work implements IWorkDefinition, this simply returns the input.\n */\nexport function getWorkDefinition<\n TName extends string,\n TData,\n TResult,\n TAvailableWorkResults extends Record<string, unknown>,\n>(\n input: WorkInput<TName, TData, TResult, TAvailableWorkResults>\n): IWorkDefinition<TName, TData, TResult, TAvailableWorkResults> {\n return input;\n}\n","import {\n IWorkflowContext,\n IWorkflowResult,\n IWorkResultsMap,\n WorkflowStatus,\n IWorkflowWork,\n IWorkDefinition,\n IWorkResult,\n WorkStatus,\n} from './workflow.types';\nimport { WorkInput, getWorkDefinition } from './work';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, IWorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]> {\n const result = this.map.get(name);\n if (!result) {\n throw new Error(\n `Work result \"${String(name)}\" not found. This work may not have executed yet.`\n );\n }\n return result as IWorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has(name: keyof TWorkResults): boolean {\n return this.map.has(name);\n }\n}\n\n/**\n * A simple, extensible workflow engine that supports serial and parallel work execution.\n * Work names and result types are automatically inferred from the workflow definition.\n *\n * @example\n * ```typescript\n * const workflow = new Workflow<{ userId: string }>()\n * .serial({\n * name: 'validate',\n * execute: async (ctx) => true, // returns boolean\n * })\n * .parallel([\n * {\n * name: 'fetchOrders',\n * execute: async (ctx) => [{ id: 1 }], // returns Order[]\n * },\n * {\n * name: 'fetchProfile',\n * execute: async (ctx) => ({ name: 'John' }), // returns Profile\n * },\n * ])\n * .serial({\n * name: 'process',\n * execute: async (ctx) => {\n * // ✅ Autocomplete for names AND types are inferred!\n * const isValid = ctx.workResults.get('validate').result; // boolean | undefined\n * const orders = ctx.workResults.get('fetchOrders').result; // Order[] | undefined\n * const profile = ctx.workResults.get('fetchProfile').result; // Profile | undefined\n * return { orders, profile };\n * },\n * });\n *\n * const result = await workflow.run({ userId: '123' });\n * ```\n */\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n\n /**\n * Add a serial work to the workflow.\n * Accepts either an inline work definition or a Work instance.\n * The work name and result type are automatically inferred.\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts either inline work definitions or Work instances.\n * All work names and result types are automatically inferred.\n */\n parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(\n works: TParallelWorks\n ): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)) as unknown as IWorkDefinition<\n string,\n TData,\n unknown,\n TWorkResults\n >[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n\n try {\n for (const workGroup of this.works) {\n if (workGroup.type === 'serial') {\n await this.executeWork(workGroup.works[0], context, workResults);\n } else {\n await this.executeParallelWorks(workGroup.works, context, workResults);\n }\n }\n\n return {\n status: WorkflowStatus.COMPLETED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n };\n } catch (error) {\n return {\n status: WorkflowStatus.FAILED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n /**\n * Execute a single work\n */\n private async executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n work: IWorkDefinition<string, TData, any, any>,\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n const skippedResult: IWorkResult = {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, skippedResult as any);\n workResults.set(work.name as keyof TWorkResults, skippedResult);\n return;\n }\n }\n\n try {\n const result = await work.execute(context);\n\n const workResult: IWorkResult = {\n status: WorkStatus.COMPLETED,\n result,\n duration: Date.now() - workStartTime,\n };\n\n // Store result in context for subsequent works\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, workResult as any);\n workResults.set(work.name as keyof TWorkResults, workResult);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n workResults.set(work.name as keyof TWorkResults, {\n status: WorkStatus.FAILED,\n error: err,\n duration: Date.now() - workStartTime,\n });\n\n // Call error handler if provided\n if (work.onError) {\n await work.onError(err, context);\n }\n\n // Re-throw to stop workflow execution\n throw err;\n }\n }\n\n /**\n * Execute multiple works in parallel\n */\n private async executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n works: IWorkDefinition<string, TData, any, any>[],\n context: IWorkflowContext<TData, TWorkResults>,\n workResults: Map<keyof TWorkResults, IWorkResult>\n ): Promise<void> {\n const promises = works.map(async (work) => {\n const workStartTime = Date.now();\n\n // Check if work should run\n if (work.shouldRun) {\n const shouldRun = await work.shouldRun(context);\n if (!shouldRun) {\n const skippedResult: IWorkResult = {\n status: WorkStatus.SKIPPED,\n duration: Date.now() - workStartTime,\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, skippedResult as any);\n workResults.set(work.name as keyof TWorkResults, skippedResult);\n return { work, skipped: true };\n }\n }\n\n try {\n const result = await work.execute(context);\n return { work, result, startTime: workStartTime };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return { work, error: err, startTime: workStartTime };\n }\n });\n\n const results = await Promise.all(promises);\n\n // Process results and check for errors\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const errors: { work: IWorkDefinition<string, TData, any, any>; error: Error }[] = [];\n\n for (const result of results) {\n if ('skipped' in result && result.skipped) {\n continue;\n }\n\n const duration = Date.now() - result.startTime!;\n\n if ('error' in result && result.error) {\n const workResult: IWorkResult = {\n status: WorkStatus.FAILED,\n error: result.error,\n duration,\n };\n workResults.set(result.work.name as keyof TWorkResults, workResult);\n errors.push({ work: result.work, error: result.error });\n } else {\n const workResult: IWorkResult = {\n status: WorkStatus.COMPLETED,\n result: result.result,\n duration,\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(result.work.name as keyof TWorkResults, workResult as any);\n workResults.set(result.work.name as keyof TWorkResults, workResult);\n }\n }\n\n // Handle errors after all parallel works complete\n if (errors.length > 0) {\n // Call error handlers\n for (const { work, error } of errors) {\n if (work.onError) {\n await work.onError(error, context);\n }\n }\n\n // Throw the first error to stop workflow\n throw errors[0].error;\n }\n }\n}\n\n/**\n * Helper type to extract work results from parallel works array.\n * Since Work implements IWorkDefinition, we can use Extract directly.\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":";AAGO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,eAAY;AACZ,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AALA,SAAAA;AAAA,GAAA;AAWL,IAAK,iBAAL,kBAAKC,oBAAL;AACL,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,eAAY;AACZ,EAAAA,gBAAA,YAAS;AAJC,SAAAA;AAAA,GAAA;;;ACML,IAAM,OAAN,MAKoE;AAAA,EAkBzE,YAAY,YAA2E;AACrF,SAAK,OAAO,WAAW;AACvB,SAAK,UAAU,WAAW;AAC1B,SAAK,YAAY,WAAW;AAC5B,SAAK,UAAU,WAAW;AAAA,EAC5B;AACF;AAiBO,SAAS,kBAMd,OAC+D;AAC/D,SAAO;AACT;;;AC5DA,IAAM,iBAAN,MAE2C;AAAA,EAF3C;AAGE,SAAQ,MAAM,oBAAI,IAA8C;AAAA;AAAA,EAEhE,IAAkC,MAAuC;AACvE,UAAM,SAAS,KAAK,IAAI,IAAI,IAAI;AAChC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,gBAAgB,OAAO,IAAI,CAAC;AAAA,MAC9B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAkC,MAAS,OAA2C;AACpF,SAAK,IAAI,IAAI,MAAM,KAAK;AAAA,EAC1B;AAAA,EAEA,IAAI,MAAmC;AACrC,WAAO,KAAK,IAAI,IAAI,IAAI;AAAA,EAC1B;AACF;AAqCO,IAAM,WAAN,MAGL;AAAA,EAHK;AAIL,SAAQ,QAAyB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,OACE,MAC2D;AAC3D,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,CAAC,kBAAkB,IAAI,CAAC;AAAA,IACjC,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SACE,OACuE;AACvE,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAM9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,aAAmE;AAC3E,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,UAAiD;AAAA,MACrD,MAAM;AAAA,MACN,aAAa,IAAI,eAA6B;AAAA,IAChD;AACA,UAAM,cAAc,oBAAI,IAAqC;AAE7D,QAAI;AACF,iBAAW,aAAa,KAAK,OAAO;AAClC,YAAI,UAAU,SAAS,UAAU;AAC/B,gBAAM,KAAK,YAAY,UAAU,MAAM,CAAC,GAAG,SAAS,WAAW;AAAA,QACjE,OAAO;AACL,gBAAM,KAAK,qBAAqB,UAAU,OAAO,SAAS,WAAW;AAAA,QACvE;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,QAC5B,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAEZ,MACA,SACA,aACe;AACf,UAAM,gBAAgB,KAAK,IAAI;AAG/B,QAAI,KAAK,WAAW;AAClB,YAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,UAAI,CAAC,WAAW;AACd,cAAM,gBAA6B;AAAA,UACjC;AAAA,UACA,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAEA,gBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,oBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AAEzC,YAAM,aAA0B;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAIA,cAAQ,YAAY,IAAI,KAAK,MAA4B,UAAiB;AAC1E,kBAAY,IAAI,KAAK,MAA4B,UAAU;AAAA,IAC7D,SAAS,OAAO;AACd,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAEpE,kBAAY,IAAI,KAAK,MAA4B;AAAA,QAC/C;AAAA,QACA,OAAO;AAAA,QACP,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB,CAAC;AAGD,UAAI,KAAK,SAAS;AAChB,cAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,MACjC;AAGA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAEZ,OACA,SACA,aACe;AACf,UAAM,WAAW,MAAM,IAAI,OAAO,SAAS;AACzC,YAAM,gBAAgB,KAAK,IAAI;AAG/B,UAAI,KAAK,WAAW;AAClB,cAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,YAAI,CAAC,WAAW;AACd,gBAAM,gBAA6B;AAAA,YACjC;AAAA,YACA,UAAU,KAAK,IAAI,IAAI;AAAA,UACzB;AAEA,kBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,sBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D,iBAAO,EAAE,MAAM,SAAS,KAAK;AAAA,QAC/B;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AACzC,eAAO,EAAE,MAAM,QAAQ,WAAW,cAAc;AAAA,MAClD,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,eAAO,EAAE,MAAM,OAAO,KAAK,WAAW,cAAc;AAAA,MACtD;AAAA,IACF,CAAC;AAED,UAAM,UAAU,MAAM,QAAQ,IAAI,QAAQ;AAI1C,UAAM,SAA6E,CAAC;AAEpF,eAAW,UAAU,SAAS;AAC5B,UAAI,aAAa,UAAU,OAAO,SAAS;AACzC;AAAA,MACF;AAEA,YAAM,WAAW,KAAK,IAAI,IAAI,OAAO;AAErC,UAAI,WAAW,UAAU,OAAO,OAAO;AACrC,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,OAAO,OAAO;AAAA,UACd;AAAA,QACF;AACA,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAClE,eAAO,KAAK,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,MACxD,OAAO;AACL,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,QAAQ,OAAO;AAAA,UACf;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAAA,MACpE;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,GAAG;AAErB,iBAAW,EAAE,MAAM,MAAM,KAAK,QAAQ;AACpC,YAAI,KAAK,SAAS;AAChB,gBAAM,KAAK,QAAQ,OAAO,OAAO;AAAA,QACnC;AAAA,MACF;AAGA,YAAM,OAAO,CAAC,EAAE;AAAA,IAClB;AAAA,EACF;AACF;","names":["WorkStatus","WorkflowStatus"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yigitahmetsahin/workflow-ts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A simple, extensible TypeScript workflow engine supporting serial and parallel work execution with full type inference",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"test": "vitest run",
|
|
22
22
|
"test:watch": "vitest",
|
|
23
23
|
"test:coverage": "vitest run --coverage",
|
|
24
|
-
"lint": "eslint src && tsc --noEmit",
|
|
25
|
-
"lint:fix": "eslint src --fix",
|
|
24
|
+
"lint": "eslint src examples && tsc --noEmit",
|
|
25
|
+
"lint:fix": "eslint src examples --fix",
|
|
26
26
|
"format": "prettier --write .",
|
|
27
27
|
"format:check": "prettier --check .",
|
|
28
28
|
"prepublishOnly": "npm run build"
|