@yigitahmetsahin/workflow-ts 3.4.1 → 3.4.3
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 +25 -49
- package/dist/index.cjs +35 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -73
- package/dist/index.d.ts +45 -73
- package/dist/index.js +35 -44
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ const workflow = new Workflow<{ userId: string }>()
|
|
|
58
58
|
name: 'process',
|
|
59
59
|
execute: async (ctx) => {
|
|
60
60
|
// ✅ Types are automatically inferred!
|
|
61
|
-
// workResults.get() returns
|
|
61
|
+
// workResults.get() returns WorkResult with status, result, duration
|
|
62
62
|
const orders = ctx.workResults.get('fetchOrders').result; // { id: number }[]
|
|
63
63
|
const profile = ctx.workResults.get('fetchProfile').result; // { name: string; email: string }
|
|
64
64
|
return { orderCount: orders?.length ?? 0, userName: profile?.name };
|
|
@@ -80,7 +80,7 @@ const workflow2 = new Workflow<{ userId: string }>().serial(validateUser).parall
|
|
|
80
80
|
|
|
81
81
|
const result = await workflow.run({ userId: 'user-123' });
|
|
82
82
|
|
|
83
|
-
if (result.status ===
|
|
83
|
+
if (result.status === 'completed') {
|
|
84
84
|
console.log('Workflow completed in', result.totalDuration, 'ms');
|
|
85
85
|
console.log('Final result:', result.context.workResults.get('process').result);
|
|
86
86
|
}
|
|
@@ -184,9 +184,9 @@ const conditionalWork = new Work({
|
|
|
184
184
|
});
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
### `.seal(
|
|
187
|
+
### `.seal(sealingWork?)`
|
|
188
188
|
|
|
189
|
-
Seal the workflow to prevent further modifications. Returns an `ISealedWorkflow` that
|
|
189
|
+
Seal the workflow to prevent further modifications. Returns an `ISealedWorkflow` that exposes `name`, `works`, `options`, `isSealed()`, and `run()`.
|
|
190
190
|
|
|
191
191
|
```typescript
|
|
192
192
|
const sealed = new Workflow<{ userId: string }>()
|
|
@@ -200,50 +200,25 @@ const sealed = new Workflow<{ userId: string }>()
|
|
|
200
200
|
])
|
|
201
201
|
.seal();
|
|
202
202
|
|
|
203
|
+
// Sealed workflow properties
|
|
204
|
+
console.log(sealed.name); // 'seal'
|
|
205
|
+
console.log(sealed.works); // readonly array of work definitions
|
|
206
|
+
console.log(sealed.options); // { failFast: true }
|
|
207
|
+
console.log(sealed.isSealed()); // true
|
|
208
|
+
|
|
203
209
|
// TypeScript prevents further modifications:
|
|
204
210
|
// sealed.serial(...) // ❌ Error: Property 'serial' does not exist
|
|
205
211
|
// sealed.parallel(...) // ❌ Error: Property 'parallel' does not exist
|
|
206
212
|
|
|
207
|
-
//
|
|
208
|
-
console.log(sealed.isSealed()); // true
|
|
209
|
-
|
|
210
|
-
// Only run() is available:
|
|
213
|
+
// Only run() is available for execution:
|
|
211
214
|
const result = await sealed.run({ userId: '123' }); // ✅ OK
|
|
212
215
|
```
|
|
213
216
|
|
|
214
|
-
#### With Sealing Work
|
|
215
|
-
|
|
216
|
-
You can pass an `ISealingWorkDefinition` (similar to `IWorkDefinition` but without `name`) to wrap the workflow execution:
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const workflow = new Workflow<{ userId: string }>()
|
|
220
|
-
.serial({ name: 'fetchUser', execute: async (ctx) => ({ id: ctx.data.userId }) });
|
|
221
|
-
|
|
222
|
-
const sealed = workflow.seal({
|
|
223
|
-
execute: async (ctx) => {
|
|
224
|
-
console.log('Before:', ctx.data.userId);
|
|
225
|
-
const result = await workflow.run(ctx.data);
|
|
226
|
-
console.log('After');
|
|
227
|
-
return result;
|
|
228
|
-
},
|
|
229
|
-
// Optional: supports same options as IWorkDefinition
|
|
230
|
-
// shouldRun: (ctx) => ctx.data.enabled,
|
|
231
|
-
// onError: (error, ctx) => console.error(error),
|
|
232
|
-
// silenceError: true,
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// Has name 'seal' and custom execute function
|
|
236
|
-
console.log(sealed.name); // 'seal'
|
|
237
|
-
await sealed.execute({ data: { userId: '123' }, workResults: ... }); // Uses custom execute
|
|
238
|
-
await sealed.run({ userId: '123' }); // Bypasses custom execute
|
|
239
|
-
```
|
|
240
|
-
|
|
241
217
|
This is useful when you want to:
|
|
242
218
|
|
|
243
219
|
- **Enforce immutability** - Ensure the workflow definition cannot be accidentally modified after construction
|
|
244
220
|
- **Expose a clean API** - Pass a sealed workflow to other parts of your code that should only execute it, not modify it
|
|
245
221
|
- **Type safety** - Get compile-time errors if someone tries to add more works to a finalized workflow
|
|
246
|
-
- **Wrap execution** - Add logging, metrics, or other cross-cutting concerns via custom execute
|
|
247
222
|
|
|
248
223
|
```typescript
|
|
249
224
|
// Example: Factory function that returns a sealed workflow
|
|
@@ -258,6 +233,7 @@ function buildUserWorkflow(): ISealedWorkflow<{ userId: string }, { user: User }
|
|
|
258
233
|
|
|
259
234
|
// Consumers can only run the workflow
|
|
260
235
|
const workflow = buildUserWorkflow();
|
|
236
|
+
console.log(workflow.name); // 'seal'
|
|
261
237
|
const result = await workflow.run({ userId: '123' });
|
|
262
238
|
```
|
|
263
239
|
|
|
@@ -280,7 +256,7 @@ const workflow = new Workflow<{ userId: string }>()
|
|
|
280
256
|
execute: async (ctx) => {
|
|
281
257
|
// Check if previous work failed
|
|
282
258
|
const optionalResult = ctx.workResults.get('fetchOptionalData');
|
|
283
|
-
if (optionalResult.status ===
|
|
259
|
+
if (optionalResult.status === 'failed') {
|
|
284
260
|
return { data: null, error: optionalResult.error?.message };
|
|
285
261
|
}
|
|
286
262
|
return { data: optionalResult.result };
|
|
@@ -288,7 +264,7 @@ const workflow = new Workflow<{ userId: string }>()
|
|
|
288
264
|
});
|
|
289
265
|
|
|
290
266
|
const result = await workflow.run({ userId: '123' });
|
|
291
|
-
// result.status ===
|
|
267
|
+
// result.status === 'completed' (workflow continues despite error)
|
|
292
268
|
```
|
|
293
269
|
|
|
294
270
|
### Workflow Options
|
|
@@ -331,7 +307,7 @@ const workflow = new Workflow<{ userId: string }>({ failFast: false })
|
|
|
331
307
|
// work3 WILL execute, but workflow still fails at the end
|
|
332
308
|
|
|
333
309
|
const result = await workflow.run({ userId: '123' });
|
|
334
|
-
// result.status ===
|
|
310
|
+
// result.status === 'failed'
|
|
335
311
|
// result.error.message === 'Continue anyway' (first error)
|
|
336
312
|
// result.context.workResults.get('work3')?.result === 'still runs'
|
|
337
313
|
```
|
|
@@ -357,7 +333,7 @@ const workflow = new Workflow<{ userId: string }>({ failFast: false })
|
|
|
357
333
|
.serial({ name: 'final', execute: async () => 'done' });
|
|
358
334
|
|
|
359
335
|
const result = await workflow.run({ userId: '123' });
|
|
360
|
-
// result.status ===
|
|
336
|
+
// result.status === 'completed' (all errors silenced)
|
|
361
337
|
```
|
|
362
338
|
|
|
363
339
|
### `.run(initialData)`
|
|
@@ -371,29 +347,29 @@ const result = await workflow.run({ userId: '123' });
|
|
|
371
347
|
### Result Object
|
|
372
348
|
|
|
373
349
|
```typescript
|
|
374
|
-
|
|
350
|
+
type WorkflowResult = {
|
|
375
351
|
status: WorkflowStatus; // 'completed' | 'failed'
|
|
376
352
|
context: {
|
|
377
353
|
data: TData; // Initial data passed to run()
|
|
378
354
|
workResults: IWorkResultsMap; // Type-safe map of work results
|
|
379
355
|
};
|
|
380
|
-
workResults: Map<string,
|
|
356
|
+
workResults: Map<string, WorkResult>; // Detailed results per work
|
|
381
357
|
totalDuration: number; // Total execution time in ms
|
|
382
358
|
error?: Error; // Error if workflow failed
|
|
383
|
-
}
|
|
359
|
+
};
|
|
384
360
|
|
|
385
361
|
// Each work result contains execution details
|
|
386
|
-
|
|
362
|
+
type WorkResult<T> = {
|
|
387
363
|
status: WorkStatus; // 'completed' | 'failed' | 'skipped'
|
|
388
364
|
result?: T; // The return value from execute()
|
|
389
365
|
error?: Error; // Error if work failed
|
|
390
366
|
duration: number; // Execution time in ms
|
|
391
|
-
}
|
|
367
|
+
};
|
|
392
368
|
```
|
|
393
369
|
|
|
394
370
|
### Accessing Work Results
|
|
395
371
|
|
|
396
|
-
`ctx.workResults.get()` returns
|
|
372
|
+
`ctx.workResults.get()` returns a `WorkResult` object, not the raw value:
|
|
397
373
|
|
|
398
374
|
```typescript
|
|
399
375
|
// Get the full work result with metadata
|
|
@@ -405,7 +381,7 @@ console.log(workResult.duration); // execution time in ms
|
|
|
405
381
|
const user = ctx.workResults.get('fetchUser').result;
|
|
406
382
|
|
|
407
383
|
// Check status before accessing result
|
|
408
|
-
if (workResult.status ===
|
|
384
|
+
if (workResult.status === 'completed') {
|
|
409
385
|
console.log('User:', workResult.result);
|
|
410
386
|
}
|
|
411
387
|
```
|
|
@@ -431,9 +407,9 @@ Skipped works are still accessible via `workResults.get()` with `status: 'skippe
|
|
|
431
407
|
|
|
432
408
|
```typescript
|
|
433
409
|
const emailResult = ctx.workResults.get('sendEmail');
|
|
434
|
-
if (emailResult.status ===
|
|
410
|
+
if (emailResult.status === 'skipped') {
|
|
435
411
|
console.log('Email was skipped');
|
|
436
|
-
} else if (emailResult.status ===
|
|
412
|
+
} else if (emailResult.status === 'completed') {
|
|
437
413
|
console.log('Email sent:', emailResult.result);
|
|
438
414
|
}
|
|
439
415
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -21,30 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Work: () => Work,
|
|
24
|
-
WorkStatus: () => WorkStatus,
|
|
25
24
|
Workflow: () => Workflow,
|
|
26
|
-
WorkflowStatus: () => WorkflowStatus,
|
|
27
25
|
getWorkDefinition: () => getWorkDefinition
|
|
28
26
|
});
|
|
29
27
|
module.exports = __toCommonJS(index_exports);
|
|
30
28
|
|
|
31
|
-
// src/workflow.types.ts
|
|
32
|
-
var WorkStatus = /* @__PURE__ */ ((WorkStatus2) => {
|
|
33
|
-
WorkStatus2["PENDING"] = "pending";
|
|
34
|
-
WorkStatus2["RUNNING"] = "running";
|
|
35
|
-
WorkStatus2["COMPLETED"] = "completed";
|
|
36
|
-
WorkStatus2["FAILED"] = "failed";
|
|
37
|
-
WorkStatus2["SKIPPED"] = "skipped";
|
|
38
|
-
return WorkStatus2;
|
|
39
|
-
})(WorkStatus || {});
|
|
40
|
-
var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
|
|
41
|
-
WorkflowStatus2["PENDING"] = "pending";
|
|
42
|
-
WorkflowStatus2["RUNNING"] = "running";
|
|
43
|
-
WorkflowStatus2["COMPLETED"] = "completed";
|
|
44
|
-
WorkflowStatus2["FAILED"] = "failed";
|
|
45
|
-
return WorkflowStatus2;
|
|
46
|
-
})(WorkflowStatus || {});
|
|
47
|
-
|
|
48
29
|
// src/work.ts
|
|
49
30
|
var Work = class {
|
|
50
31
|
constructor(definition) {
|
|
@@ -82,9 +63,21 @@ var WorkResultsMap = class {
|
|
|
82
63
|
};
|
|
83
64
|
var Workflow = class {
|
|
84
65
|
constructor(options = {}) {
|
|
85
|
-
this.
|
|
66
|
+
this._works = [];
|
|
86
67
|
this._sealed = false;
|
|
87
|
-
this.
|
|
68
|
+
this._options = { failFast: true, ...options };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The list of works in the workflow (readonly)
|
|
72
|
+
*/
|
|
73
|
+
get works() {
|
|
74
|
+
return this._works;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The workflow options (readonly)
|
|
78
|
+
*/
|
|
79
|
+
get options() {
|
|
80
|
+
return this._options;
|
|
88
81
|
}
|
|
89
82
|
/**
|
|
90
83
|
* Check if the workflow is sealed
|
|
@@ -102,7 +95,7 @@ var Workflow = class {
|
|
|
102
95
|
if (this._sealed) {
|
|
103
96
|
throw new Error("Cannot add work to a sealed workflow");
|
|
104
97
|
}
|
|
105
|
-
this.
|
|
98
|
+
this._works.push({
|
|
106
99
|
type: "serial",
|
|
107
100
|
works: [getWorkDefinition(work)]
|
|
108
101
|
});
|
|
@@ -126,23 +119,21 @@ var Workflow = class {
|
|
|
126
119
|
if (this._sealed) {
|
|
127
120
|
throw new Error("Cannot add work to a sealed workflow");
|
|
128
121
|
}
|
|
129
|
-
this.
|
|
122
|
+
this._works.push({
|
|
130
123
|
type: "parallel",
|
|
131
124
|
works: works.map((w) => getWorkDefinition(w))
|
|
132
125
|
});
|
|
133
126
|
return this;
|
|
134
127
|
}
|
|
135
|
-
seal(
|
|
128
|
+
seal(_sealingWork) {
|
|
136
129
|
this._sealed = true;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
return this;
|
|
130
|
+
return {
|
|
131
|
+
name: "seal",
|
|
132
|
+
works: this._works,
|
|
133
|
+
options: this._options,
|
|
134
|
+
isSealed: () => this._sealed,
|
|
135
|
+
run: this.run.bind(this)
|
|
136
|
+
};
|
|
146
137
|
}
|
|
147
138
|
/**
|
|
148
139
|
* Execute the workflow with initial data
|
|
@@ -156,7 +147,7 @@ var Workflow = class {
|
|
|
156
147
|
const workResults = /* @__PURE__ */ new Map();
|
|
157
148
|
const collectedErrors = [];
|
|
158
149
|
try {
|
|
159
|
-
for (const workGroup of this.
|
|
150
|
+
for (const workGroup of this._works) {
|
|
160
151
|
try {
|
|
161
152
|
if (workGroup.type === "serial") {
|
|
162
153
|
await this.executeWork(workGroup.works[0], context, workResults);
|
|
@@ -165,7 +156,7 @@ var Workflow = class {
|
|
|
165
156
|
}
|
|
166
157
|
} catch (error) {
|
|
167
158
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
168
|
-
if (this.
|
|
159
|
+
if (this._options.failFast) {
|
|
169
160
|
throw err;
|
|
170
161
|
}
|
|
171
162
|
collectedErrors.push(err);
|
|
@@ -173,7 +164,7 @@ var Workflow = class {
|
|
|
173
164
|
}
|
|
174
165
|
if (collectedErrors.length > 0) {
|
|
175
166
|
return {
|
|
176
|
-
status: "failed"
|
|
167
|
+
status: "failed",
|
|
177
168
|
context,
|
|
178
169
|
workResults,
|
|
179
170
|
totalDuration: Date.now() - startTime,
|
|
@@ -181,14 +172,14 @@ var Workflow = class {
|
|
|
181
172
|
};
|
|
182
173
|
}
|
|
183
174
|
return {
|
|
184
|
-
status: "completed"
|
|
175
|
+
status: "completed",
|
|
185
176
|
context,
|
|
186
177
|
workResults,
|
|
187
178
|
totalDuration: Date.now() - startTime
|
|
188
179
|
};
|
|
189
180
|
} catch (error) {
|
|
190
181
|
return {
|
|
191
|
-
status: "failed"
|
|
182
|
+
status: "failed",
|
|
192
183
|
context,
|
|
193
184
|
workResults,
|
|
194
185
|
totalDuration: Date.now() - startTime,
|
|
@@ -205,7 +196,7 @@ var Workflow = class {
|
|
|
205
196
|
const shouldRun = await work.shouldRun(context);
|
|
206
197
|
if (!shouldRun) {
|
|
207
198
|
const skippedResult = {
|
|
208
|
-
status: "skipped"
|
|
199
|
+
status: "skipped",
|
|
209
200
|
duration: Date.now() - workStartTime
|
|
210
201
|
};
|
|
211
202
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -216,7 +207,7 @@ var Workflow = class {
|
|
|
216
207
|
try {
|
|
217
208
|
const result = await work.execute(context);
|
|
218
209
|
const workResult = {
|
|
219
|
-
status: "completed"
|
|
210
|
+
status: "completed",
|
|
220
211
|
result,
|
|
221
212
|
duration: Date.now() - workStartTime
|
|
222
213
|
};
|
|
@@ -225,7 +216,7 @@ var Workflow = class {
|
|
|
225
216
|
} catch (error) {
|
|
226
217
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
227
218
|
const failedResult = {
|
|
228
|
-
status: "failed"
|
|
219
|
+
status: "failed",
|
|
229
220
|
error: err,
|
|
230
221
|
duration: Date.now() - workStartTime
|
|
231
222
|
};
|
|
@@ -249,7 +240,7 @@ var Workflow = class {
|
|
|
249
240
|
const shouldRun = await work.shouldRun(context);
|
|
250
241
|
if (!shouldRun) {
|
|
251
242
|
const skippedResult = {
|
|
252
|
-
status: "skipped"
|
|
243
|
+
status: "skipped",
|
|
253
244
|
duration: Date.now() - workStartTime
|
|
254
245
|
};
|
|
255
246
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -274,7 +265,7 @@ var Workflow = class {
|
|
|
274
265
|
const duration = Date.now() - result.startTime;
|
|
275
266
|
if ("error" in result && result.error) {
|
|
276
267
|
const workResult = {
|
|
277
|
-
status: "failed"
|
|
268
|
+
status: "failed",
|
|
278
269
|
error: result.error,
|
|
279
270
|
duration
|
|
280
271
|
};
|
|
@@ -285,7 +276,7 @@ var Workflow = class {
|
|
|
285
276
|
}
|
|
286
277
|
} else {
|
|
287
278
|
const workResult = {
|
|
288
|
-
status: "completed"
|
|
279
|
+
status: "completed",
|
|
289
280
|
result: result.result,
|
|
290
281
|
duration
|
|
291
282
|
};
|
|
@@ -306,9 +297,7 @@ var Workflow = class {
|
|
|
306
297
|
// Annotate the CommonJS export names for ESM import in node:
|
|
307
298
|
0 && (module.exports = {
|
|
308
299
|
Work,
|
|
309
|
-
WorkStatus,
|
|
310
300
|
Workflow,
|
|
311
|
-
WorkflowStatus,
|
|
312
301
|
getWorkDefinition
|
|
313
302
|
});
|
|
314
303
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 a work result with compile-time type checking */\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 /** Check if a work result exists */\n has(name: string): 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 /** Optional: if true, errors won't stop the workflow (result will be undefined) */\n silenceError?: boolean;\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\n/**\n * Interface for the Workflow class.\n * Defines all methods available on a workflow before sealing.\n */\nexport interface IWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Add a serial work to the workflow\n */\n serial<TName extends string, TResult>(\n work: IWorkDefinition<TName, TData, TResult, TWorkResults>\n ): IWorkflow<TData, TWorkResults & { [K in TName]: TResult }>;\n\n /**\n * Add parallel works to the workflow\n */\n\n parallel(\n works: readonly IWorkDefinition<string, TData, any, TWorkResults>[]\n ): IWorkflow<TData, any>;\n\n /**\n * Seal the workflow to prevent further modifications\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults>\n ): ISealedWorkflowWithExecute<TData, TWorkResults>;\n\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean;\n\n /**\n * Execute the workflow with initial data\n */\n run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;\n}\n\n/**\n * A limited work definition for sealing a workflow.\n * Similar to IWorkDefinition but without 'name' (hardcoded as 'seal').\n * The execute function returns IWorkflowResult instead of a custom result type.\n */\nexport type ISealingWorkDefinition<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<\n IWorkDefinition<'seal', TData, IWorkflowResult<TData, TWorkResults>, TWorkResults>,\n 'name'\n>;\n\n/**\n * A sealed workflow that can only be executed, not modified.\n * Use workflow.seal() to create a sealed workflow.\n */\nexport interface ISealedWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean;\n\n /**\n * Execute the workflow with initial data\n */\n run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;\n}\n\n/**\n * A sealed workflow with a custom execute function.\n * Created when seal() is called with an execute option.\n */\nexport interface ISealedWorkflowWithExecute<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> extends ISealedWorkflow<TData, TWorkResults> {\n /**\n * Hardcoded name for the sealed workflow\n */\n readonly name: 'seal';\n\n /**\n * Custom execute function provided during sealing.\n * Receives context (like a Work).\n */\n execute(\n context: IWorkflowContext<TData, TWorkResults>\n ): Promise<IWorkflowResult<TData, TWorkResults>>;\n}\n\n/**\n * Options for configuring workflow behavior\n */\nexport interface WorkflowOptions {\n /**\n * Whether to stop execution immediately when a work fails.\n * - true: Stop on first failure (default)\n * - false: Continue executing remaining works, fail at the end if any work failed\n * @default true\n */\n failFast?: boolean;\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 */\nexport type 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","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 /** Optional: if true, errors won't stop the workflow (result will be undefined) */\n readonly silenceError?: boolean;\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 this.silenceError = definition.silenceError;\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 IWorkflow,\n ISealedWorkflow,\n ISealingWorkDefinition,\n ISealedWorkflowWithExecute,\n WorkflowOptions,\n ParallelWorksToRecord,\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<K extends keyof TWorkResults>(name: K): 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> implements IWorkflow<TData, TWorkResults> {\n private works: IWorkflowWork[] = [];\n private options: Required<WorkflowOptions>;\n private _sealed = false;\n\n constructor(options: WorkflowOptions = {}) {\n this.options = { failFast: true, ...options };\n }\n\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean {\n return this._sealed;\n }\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 * @throws Error if the workflow is sealed\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\n * @throws Error if the workflow is sealed\n *\n * @example\n * ```typescript\n * workflow.parallel([\n * { name: 'work1', execute: async () => 'result1' },\n * { name: 'work2', execute: async () => 123 },\n * ]);\n * ```\n */\n parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(\n works: TParallelWorks\n ): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)),\n });\n return this as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Seal the workflow to prevent further modifications.\n * Returns a SealedWorkflow that can only be executed with run().\n *\n * @example\n * ```typescript\n * // Without options - returns ISealedWorkflow\n * const sealed = new Workflow<{ userId: string }>()\n * .serial({ name: 'step1', execute: async () => 'result' })\n * .seal();\n *\n * sealed.isSealed(); // true\n * await sealed.run({ userId: '123' }); // OK\n *\n * // With sealing work - returns ISealedWorkflowWithExecute\n * const sealedWithExecute = workflow.seal({\n * execute: async (ctx) => {\n * console.log('Before:', ctx.data);\n * const result = await workflow.run(ctx.data);\n * console.log('After');\n * return result;\n * },\n * // Optional: shouldRun, onError, silenceError (like IWorkDefinition)\n * });\n *\n * sealedWithExecute.name; // 'seal'\n * await sealedWithExecute.execute({ data: initialData, workResults: ... });\n * ```\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults>\n ): ISealedWorkflowWithExecute<TData, TWorkResults>;\n seal(\n sealingWork?: ISealingWorkDefinition<TData, TWorkResults>\n ): ISealedWorkflow<TData, TWorkResults> | ISealedWorkflowWithExecute<TData, TWorkResults> {\n this._sealed = true;\n if (sealingWork?.execute) {\n return {\n name: 'seal',\n isSealed: () => this._sealed,\n run: this.run.bind(this),\n execute: sealingWork.execute,\n };\n }\n return this;\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 const collectedErrors: Error[] = [];\n\n try {\n for (const workGroup of this.works) {\n try {\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 } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n if (this.options.failFast) {\n throw err;\n }\n collectedErrors.push(err);\n }\n }\n\n // If failFast is false, check for collected errors\n if (collectedErrors.length > 0) {\n return {\n status: WorkflowStatus.FAILED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: collectedErrors[0],\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 const failedResult: IWorkResult = {\n status: WorkStatus.FAILED,\n error: err,\n duration: Date.now() - workStartTime,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, failedResult as any);\n workResults.set(work.name as keyof TWorkResults, failedResult);\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 (unless silenceError is true)\n if (!work.silenceError) {\n throw err;\n }\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 // 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 // Only track as error if silenceError is not set\n if (!result.work.silenceError) {\n errors.push({ work: result.work, error: result.error });\n }\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 // Call error handlers for all failed works (including silenced ones)\n for (const result of results) {\n if ('error' in result && result.error && result.work.onError) {\n await result.work.onError(result.error, context);\n }\n }\n\n // Throw the first non-silenced error to stop workflow\n if (errors.length > 0) {\n throw errors[0].error;\n }\n }\n}\n"],"mappings":"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","names":["WorkStatus","WorkflowStatus"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/work.ts","../src/workflow.ts"],"sourcesContent":["export * from './workflow';\nexport * from './workflow.types';\nexport * from './work';\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 /** Optional: if true, errors won't stop the workflow (result will be undefined) */\n readonly silenceError?: boolean;\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 this.silenceError = definition.silenceError;\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 WorkflowResult,\n IWorkResultsMap,\n WorkflowWork,\n IWorkDefinition,\n WorkResult,\n IWorkflow,\n ISealedWorkflow,\n ISealingWorkDefinition,\n WorkflowOptions,\n ParallelWorksToRecord,\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, WorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): WorkResult<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 WorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: WorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has<K extends keyof TWorkResults>(name: K): 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> implements IWorkflow<TData, TWorkResults> {\n private _works: WorkflowWork[] = [];\n private _options: Required<WorkflowOptions>;\n private _sealed = false;\n\n constructor(options: WorkflowOptions = {}) {\n this._options = { failFast: true, ...options };\n }\n\n /**\n * The list of works in the workflow (readonly)\n */\n get works(): readonly WorkflowWork[] {\n return this._works;\n }\n\n /**\n * The workflow options (readonly)\n */\n get options(): Readonly<Required<WorkflowOptions>> {\n return this._options;\n }\n\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean {\n return this._sealed;\n }\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 * @throws Error if the workflow is sealed\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this._works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\n * @throws Error if the workflow is sealed\n *\n * @example\n * ```typescript\n * workflow.parallel([\n * { name: 'work1', execute: async () => 'result1' },\n * { name: 'work2', execute: async () => 123 },\n * ]);\n * ```\n */\n parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(\n works: TParallelWorks\n ): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this._works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)),\n });\n return this as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Seal the workflow to prevent further modifications.\n * Returns a SealedWorkflow that can only be executed with run().\n *\n * @example\n * ```typescript\n * const sealed = new Workflow<{ userId: string }>()\n * .serial({ name: 'step1', execute: async () => 'result' })\n * .seal();\n *\n * sealed.name; // 'seal'\n * sealed.isSealed(); // true\n * await sealed.run({ userId: '123' }); // OK\n *\n * // TypeScript prevents modifications:\n * // sealed.serial(...) // Error: Property 'serial' does not exist\n * ```\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal<TResult>(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>\n ): ISealedWorkflow<TData, TWorkResults>;\n seal<TResult>(\n _sealingWork?: ISealingWorkDefinition<TData, TWorkResults, TResult>\n ): ISealedWorkflow<TData, TWorkResults> {\n this._sealed = true;\n return {\n name: 'seal',\n works: this._works,\n options: this._options,\n isSealed: () => this._sealed,\n run: this.run.bind(this),\n };\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<WorkflowResult<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, WorkResult>();\n const collectedErrors: Error[] = [];\n\n try {\n for (const workGroup of this._works) {\n try {\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 } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n if (this._options.failFast) {\n throw err;\n }\n collectedErrors.push(err);\n }\n }\n\n // If failFast is false, check for collected errors\n if (collectedErrors.length > 0) {\n return {\n status: 'failed',\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: collectedErrors[0],\n };\n }\n\n return {\n status: 'completed',\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n };\n } catch (error) {\n return {\n status: '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, WorkResult>\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: WorkResult = {\n status: '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: WorkResult = {\n status: '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 const failedResult: WorkResult = {\n status: 'failed',\n error: err,\n duration: Date.now() - workStartTime,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, failedResult as any);\n workResults.set(work.name as keyof TWorkResults, failedResult);\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 (unless silenceError is true)\n if (!work.silenceError) {\n throw err;\n }\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, WorkResult>\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: WorkResult = {\n status: '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: WorkResult = {\n status: 'failed',\n error: result.error,\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 // Only track as error if silenceError is not set\n if (!result.work.silenceError) {\n errors.push({ work: result.work, error: result.error });\n }\n } else {\n const workResult: WorkResult = {\n status: '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 // Call error handlers for all failed works (including silenced ones)\n for (const result of results) {\n if ('error' in result && result.error && result.work.onError) {\n await result.work.onError(result.error, context);\n }\n }\n\n // Throw the first non-silenced error to stop workflow\n if (errors.length > 0) {\n throw errors[0].error;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Work Status
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
PENDING = "pending",
|
|
6
|
-
RUNNING = "running",
|
|
7
|
-
COMPLETED = "completed",
|
|
8
|
-
FAILED = "failed",
|
|
9
|
-
SKIPPED = "skipped"
|
|
10
|
-
}
|
|
4
|
+
type WorkStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
11
5
|
/**
|
|
12
6
|
* Workflow Status
|
|
13
7
|
*/
|
|
14
|
-
|
|
15
|
-
PENDING = "pending",
|
|
16
|
-
RUNNING = "running",
|
|
17
|
-
COMPLETED = "completed",
|
|
18
|
-
FAILED = "failed"
|
|
19
|
-
}
|
|
8
|
+
type WorkflowStatus = 'pending' | 'running' | 'completed' | 'failed';
|
|
20
9
|
/**
|
|
21
10
|
* Context passed between workflow works
|
|
22
11
|
* TData is the type of shared data between works
|
|
@@ -33,20 +22,20 @@ interface IWorkflowContext<TData = Record<string, unknown>, TWorkResults extends
|
|
|
33
22
|
*/
|
|
34
23
|
interface IWorkResultsMap<TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
|
|
35
24
|
/** Get a work result with compile-time type checking */
|
|
36
|
-
get<K extends keyof TWorkResults>(name: K):
|
|
37
|
-
set<K extends keyof TWorkResults>(name: K, value:
|
|
25
|
+
get<K extends keyof TWorkResults>(name: K): WorkResult<TWorkResults[K]>;
|
|
26
|
+
set<K extends keyof TWorkResults>(name: K, value: WorkResult<TWorkResults[K]>): void;
|
|
38
27
|
/** Check if a work result exists */
|
|
39
28
|
has(name: string): boolean;
|
|
40
29
|
}
|
|
41
30
|
/**
|
|
42
31
|
* Result of a single work execution
|
|
43
32
|
*/
|
|
44
|
-
|
|
33
|
+
type WorkResult<TResult = unknown> = {
|
|
45
34
|
status: WorkStatus;
|
|
46
35
|
result?: TResult;
|
|
47
36
|
error?: Error;
|
|
48
37
|
duration: number;
|
|
49
|
-
}
|
|
38
|
+
};
|
|
50
39
|
/**
|
|
51
40
|
* Definition of a work with inferred name and result type
|
|
52
41
|
*/
|
|
@@ -65,25 +54,33 @@ interface IWorkDefinition<TName extends string, TData = Record<string, unknown>,
|
|
|
65
54
|
/**
|
|
66
55
|
* Internal work representation
|
|
67
56
|
*/
|
|
68
|
-
|
|
57
|
+
type WorkflowWork = {
|
|
69
58
|
type: 'serial' | 'parallel';
|
|
70
59
|
works: IWorkDefinition<string, any, any, any>[];
|
|
71
|
-
}
|
|
60
|
+
};
|
|
72
61
|
/**
|
|
73
62
|
* Result of workflow execution
|
|
74
63
|
*/
|
|
75
|
-
|
|
64
|
+
type WorkflowResult<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> = {
|
|
76
65
|
status: WorkflowStatus;
|
|
77
66
|
context: IWorkflowContext<TData, TWorkResults>;
|
|
78
|
-
workResults: Map<keyof TWorkResults,
|
|
67
|
+
workResults: Map<keyof TWorkResults, WorkResult>;
|
|
79
68
|
totalDuration: number;
|
|
80
69
|
error?: Error;
|
|
81
|
-
}
|
|
70
|
+
};
|
|
82
71
|
/**
|
|
83
72
|
* Interface for the Workflow class.
|
|
84
73
|
* Defines all methods available on a workflow before sealing.
|
|
85
74
|
*/
|
|
86
75
|
interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
|
|
76
|
+
/**
|
|
77
|
+
* The list of works in the workflow (readonly)
|
|
78
|
+
*/
|
|
79
|
+
readonly works: readonly WorkflowWork[];
|
|
80
|
+
/**
|
|
81
|
+
* The workflow options (readonly)
|
|
82
|
+
*/
|
|
83
|
+
readonly options: Readonly<Required<WorkflowOptions>>;
|
|
87
84
|
/**
|
|
88
85
|
* Add a serial work to the workflow
|
|
89
86
|
*/
|
|
@@ -98,7 +95,7 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
98
95
|
* Seal the workflow to prevent further modifications
|
|
99
96
|
*/
|
|
100
97
|
seal(): ISealedWorkflow<TData, TWorkResults>;
|
|
101
|
-
seal(sealingWork: ISealingWorkDefinition<TData, TWorkResults>):
|
|
98
|
+
seal<TResult>(sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>): ISealedWorkflow<TData, TWorkResults>;
|
|
102
99
|
/**
|
|
103
100
|
* Check if the workflow is sealed
|
|
104
101
|
*/
|
|
@@ -106,47 +103,25 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
106
103
|
/**
|
|
107
104
|
* Execute the workflow with initial data
|
|
108
105
|
*/
|
|
109
|
-
run(initialData: TData): Promise<
|
|
106
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
110
107
|
}
|
|
111
108
|
/**
|
|
112
|
-
* A
|
|
113
|
-
* Similar to IWorkDefinition but without 'name'
|
|
114
|
-
* The execute function returns IWorkflowResult instead of a custom result type.
|
|
109
|
+
* A work definition for sealing a workflow.
|
|
110
|
+
* Similar to IWorkDefinition but without 'name'.
|
|
115
111
|
*/
|
|
116
|
-
type ISealingWorkDefinition<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown
|
|
112
|
+
type ISealingWorkDefinition<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = Omit<IWorkDefinition<'seal', TData, TResult, TWorkResults>, 'name'>;
|
|
117
113
|
/**
|
|
118
114
|
* A sealed workflow that can only be executed, not modified.
|
|
119
115
|
* Use workflow.seal() to create a sealed workflow.
|
|
116
|
+
* Picks `works`, `options`, `isSealed`, and `run` from IWorkflow, adds `name: 'seal'`.
|
|
120
117
|
*/
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Check if the workflow is sealed
|
|
124
|
-
*/
|
|
125
|
-
isSealed(): boolean;
|
|
126
|
-
/**
|
|
127
|
-
* Execute the workflow with initial data
|
|
128
|
-
*/
|
|
129
|
-
run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* A sealed workflow with a custom execute function.
|
|
133
|
-
* Created when seal() is called with an execute option.
|
|
134
|
-
*/
|
|
135
|
-
interface ISealedWorkflowWithExecute<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> extends ISealedWorkflow<TData, TWorkResults> {
|
|
136
|
-
/**
|
|
137
|
-
* Hardcoded name for the sealed workflow
|
|
138
|
-
*/
|
|
118
|
+
type ISealedWorkflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> = Pick<IWorkflow<TData, TWorkResults>, 'works' | 'options' | 'isSealed' | 'run'> & {
|
|
139
119
|
readonly name: 'seal';
|
|
140
|
-
|
|
141
|
-
* Custom execute function provided during sealing.
|
|
142
|
-
* Receives context (like a Work).
|
|
143
|
-
*/
|
|
144
|
-
execute(context: IWorkflowContext<TData, TWorkResults>): Promise<IWorkflowResult<TData, TWorkResults>>;
|
|
145
|
-
}
|
|
120
|
+
};
|
|
146
121
|
/**
|
|
147
122
|
* Options for configuring workflow behavior
|
|
148
123
|
*/
|
|
149
|
-
|
|
124
|
+
type WorkflowOptions = {
|
|
150
125
|
/**
|
|
151
126
|
* Whether to stop execution immediately when a work fails.
|
|
152
127
|
* - true: Stop on first failure (default)
|
|
@@ -154,7 +129,7 @@ interface WorkflowOptions {
|
|
|
154
129
|
* @default true
|
|
155
130
|
*/
|
|
156
131
|
failFast?: boolean;
|
|
157
|
-
}
|
|
132
|
+
};
|
|
158
133
|
/**
|
|
159
134
|
* Helper type to extract work results from parallel works array.
|
|
160
135
|
* Since Work implements IWorkDefinition, we can use Extract directly.
|
|
@@ -243,10 +218,18 @@ declare function getWorkDefinition<TName extends string, TData, TResult, TAvaila
|
|
|
243
218
|
* ```
|
|
244
219
|
*/
|
|
245
220
|
declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = NonNullable<unknown>> implements IWorkflow<TData, TWorkResults> {
|
|
246
|
-
private
|
|
247
|
-
private
|
|
221
|
+
private _works;
|
|
222
|
+
private _options;
|
|
248
223
|
private _sealed;
|
|
249
224
|
constructor(options?: WorkflowOptions);
|
|
225
|
+
/**
|
|
226
|
+
* The list of works in the workflow (readonly)
|
|
227
|
+
*/
|
|
228
|
+
get works(): readonly WorkflowWork[];
|
|
229
|
+
/**
|
|
230
|
+
* The workflow options (readonly)
|
|
231
|
+
*/
|
|
232
|
+
get options(): Readonly<Required<WorkflowOptions>>;
|
|
250
233
|
/**
|
|
251
234
|
* Check if the workflow is sealed
|
|
252
235
|
*/
|
|
@@ -281,35 +264,24 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
281
264
|
*
|
|
282
265
|
* @example
|
|
283
266
|
* ```typescript
|
|
284
|
-
* // Without options - returns ISealedWorkflow
|
|
285
267
|
* const sealed = new Workflow<{ userId: string }>()
|
|
286
268
|
* .serial({ name: 'step1', execute: async () => 'result' })
|
|
287
269
|
* .seal();
|
|
288
270
|
*
|
|
271
|
+
* sealed.name; // 'seal'
|
|
289
272
|
* sealed.isSealed(); // true
|
|
290
273
|
* await sealed.run({ userId: '123' }); // OK
|
|
291
274
|
*
|
|
292
|
-
* //
|
|
293
|
-
*
|
|
294
|
-
* execute: async (ctx) => {
|
|
295
|
-
* console.log('Before:', ctx.data);
|
|
296
|
-
* const result = await workflow.run(ctx.data);
|
|
297
|
-
* console.log('After');
|
|
298
|
-
* return result;
|
|
299
|
-
* },
|
|
300
|
-
* // Optional: shouldRun, onError, silenceError (like IWorkDefinition)
|
|
301
|
-
* });
|
|
302
|
-
*
|
|
303
|
-
* sealedWithExecute.name; // 'seal'
|
|
304
|
-
* await sealedWithExecute.execute({ data: initialData, workResults: ... });
|
|
275
|
+
* // TypeScript prevents modifications:
|
|
276
|
+
* // sealed.serial(...) // Error: Property 'serial' does not exist
|
|
305
277
|
* ```
|
|
306
278
|
*/
|
|
307
279
|
seal(): ISealedWorkflow<TData, TWorkResults>;
|
|
308
|
-
seal(sealingWork: ISealingWorkDefinition<TData, TWorkResults>):
|
|
280
|
+
seal<TResult>(sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>): ISealedWorkflow<TData, TWorkResults>;
|
|
309
281
|
/**
|
|
310
282
|
* Execute the workflow with initial data
|
|
311
283
|
*/
|
|
312
|
-
run(initialData: TData): Promise<
|
|
284
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
313
285
|
/**
|
|
314
286
|
* Execute a single work
|
|
315
287
|
*/
|
|
@@ -320,4 +292,4 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
320
292
|
private executeParallelWorks;
|
|
321
293
|
}
|
|
322
294
|
|
|
323
|
-
export { type ISealedWorkflow, type
|
|
295
|
+
export { type ISealedWorkflow, type ISealingWorkDefinition, type IWorkDefinition, type IWorkResultsMap, type IWorkflow, type IWorkflowContext, type ParallelWorksToRecord, Work, type WorkInput, type WorkResult, type WorkStatus, Workflow, type WorkflowOptions, type WorkflowResult, type WorkflowStatus, type WorkflowWork, getWorkDefinition };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Work Status
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
PENDING = "pending",
|
|
6
|
-
RUNNING = "running",
|
|
7
|
-
COMPLETED = "completed",
|
|
8
|
-
FAILED = "failed",
|
|
9
|
-
SKIPPED = "skipped"
|
|
10
|
-
}
|
|
4
|
+
type WorkStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
11
5
|
/**
|
|
12
6
|
* Workflow Status
|
|
13
7
|
*/
|
|
14
|
-
|
|
15
|
-
PENDING = "pending",
|
|
16
|
-
RUNNING = "running",
|
|
17
|
-
COMPLETED = "completed",
|
|
18
|
-
FAILED = "failed"
|
|
19
|
-
}
|
|
8
|
+
type WorkflowStatus = 'pending' | 'running' | 'completed' | 'failed';
|
|
20
9
|
/**
|
|
21
10
|
* Context passed between workflow works
|
|
22
11
|
* TData is the type of shared data between works
|
|
@@ -33,20 +22,20 @@ interface IWorkflowContext<TData = Record<string, unknown>, TWorkResults extends
|
|
|
33
22
|
*/
|
|
34
23
|
interface IWorkResultsMap<TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
|
|
35
24
|
/** Get a work result with compile-time type checking */
|
|
36
|
-
get<K extends keyof TWorkResults>(name: K):
|
|
37
|
-
set<K extends keyof TWorkResults>(name: K, value:
|
|
25
|
+
get<K extends keyof TWorkResults>(name: K): WorkResult<TWorkResults[K]>;
|
|
26
|
+
set<K extends keyof TWorkResults>(name: K, value: WorkResult<TWorkResults[K]>): void;
|
|
38
27
|
/** Check if a work result exists */
|
|
39
28
|
has(name: string): boolean;
|
|
40
29
|
}
|
|
41
30
|
/**
|
|
42
31
|
* Result of a single work execution
|
|
43
32
|
*/
|
|
44
|
-
|
|
33
|
+
type WorkResult<TResult = unknown> = {
|
|
45
34
|
status: WorkStatus;
|
|
46
35
|
result?: TResult;
|
|
47
36
|
error?: Error;
|
|
48
37
|
duration: number;
|
|
49
|
-
}
|
|
38
|
+
};
|
|
50
39
|
/**
|
|
51
40
|
* Definition of a work with inferred name and result type
|
|
52
41
|
*/
|
|
@@ -65,25 +54,33 @@ interface IWorkDefinition<TName extends string, TData = Record<string, unknown>,
|
|
|
65
54
|
/**
|
|
66
55
|
* Internal work representation
|
|
67
56
|
*/
|
|
68
|
-
|
|
57
|
+
type WorkflowWork = {
|
|
69
58
|
type: 'serial' | 'parallel';
|
|
70
59
|
works: IWorkDefinition<string, any, any, any>[];
|
|
71
|
-
}
|
|
60
|
+
};
|
|
72
61
|
/**
|
|
73
62
|
* Result of workflow execution
|
|
74
63
|
*/
|
|
75
|
-
|
|
64
|
+
type WorkflowResult<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> = {
|
|
76
65
|
status: WorkflowStatus;
|
|
77
66
|
context: IWorkflowContext<TData, TWorkResults>;
|
|
78
|
-
workResults: Map<keyof TWorkResults,
|
|
67
|
+
workResults: Map<keyof TWorkResults, WorkResult>;
|
|
79
68
|
totalDuration: number;
|
|
80
69
|
error?: Error;
|
|
81
|
-
}
|
|
70
|
+
};
|
|
82
71
|
/**
|
|
83
72
|
* Interface for the Workflow class.
|
|
84
73
|
* Defines all methods available on a workflow before sealing.
|
|
85
74
|
*/
|
|
86
75
|
interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
|
|
76
|
+
/**
|
|
77
|
+
* The list of works in the workflow (readonly)
|
|
78
|
+
*/
|
|
79
|
+
readonly works: readonly WorkflowWork[];
|
|
80
|
+
/**
|
|
81
|
+
* The workflow options (readonly)
|
|
82
|
+
*/
|
|
83
|
+
readonly options: Readonly<Required<WorkflowOptions>>;
|
|
87
84
|
/**
|
|
88
85
|
* Add a serial work to the workflow
|
|
89
86
|
*/
|
|
@@ -98,7 +95,7 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
98
95
|
* Seal the workflow to prevent further modifications
|
|
99
96
|
*/
|
|
100
97
|
seal(): ISealedWorkflow<TData, TWorkResults>;
|
|
101
|
-
seal(sealingWork: ISealingWorkDefinition<TData, TWorkResults>):
|
|
98
|
+
seal<TResult>(sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>): ISealedWorkflow<TData, TWorkResults>;
|
|
102
99
|
/**
|
|
103
100
|
* Check if the workflow is sealed
|
|
104
101
|
*/
|
|
@@ -106,47 +103,25 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
106
103
|
/**
|
|
107
104
|
* Execute the workflow with initial data
|
|
108
105
|
*/
|
|
109
|
-
run(initialData: TData): Promise<
|
|
106
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
110
107
|
}
|
|
111
108
|
/**
|
|
112
|
-
* A
|
|
113
|
-
* Similar to IWorkDefinition but without 'name'
|
|
114
|
-
* The execute function returns IWorkflowResult instead of a custom result type.
|
|
109
|
+
* A work definition for sealing a workflow.
|
|
110
|
+
* Similar to IWorkDefinition but without 'name'.
|
|
115
111
|
*/
|
|
116
|
-
type ISealingWorkDefinition<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown
|
|
112
|
+
type ISealingWorkDefinition<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = Omit<IWorkDefinition<'seal', TData, TResult, TWorkResults>, 'name'>;
|
|
117
113
|
/**
|
|
118
114
|
* A sealed workflow that can only be executed, not modified.
|
|
119
115
|
* Use workflow.seal() to create a sealed workflow.
|
|
116
|
+
* Picks `works`, `options`, `isSealed`, and `run` from IWorkflow, adds `name: 'seal'`.
|
|
120
117
|
*/
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Check if the workflow is sealed
|
|
124
|
-
*/
|
|
125
|
-
isSealed(): boolean;
|
|
126
|
-
/**
|
|
127
|
-
* Execute the workflow with initial data
|
|
128
|
-
*/
|
|
129
|
-
run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* A sealed workflow with a custom execute function.
|
|
133
|
-
* Created when seal() is called with an execute option.
|
|
134
|
-
*/
|
|
135
|
-
interface ISealedWorkflowWithExecute<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> extends ISealedWorkflow<TData, TWorkResults> {
|
|
136
|
-
/**
|
|
137
|
-
* Hardcoded name for the sealed workflow
|
|
138
|
-
*/
|
|
118
|
+
type ISealedWorkflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> = Pick<IWorkflow<TData, TWorkResults>, 'works' | 'options' | 'isSealed' | 'run'> & {
|
|
139
119
|
readonly name: 'seal';
|
|
140
|
-
|
|
141
|
-
* Custom execute function provided during sealing.
|
|
142
|
-
* Receives context (like a Work).
|
|
143
|
-
*/
|
|
144
|
-
execute(context: IWorkflowContext<TData, TWorkResults>): Promise<IWorkflowResult<TData, TWorkResults>>;
|
|
145
|
-
}
|
|
120
|
+
};
|
|
146
121
|
/**
|
|
147
122
|
* Options for configuring workflow behavior
|
|
148
123
|
*/
|
|
149
|
-
|
|
124
|
+
type WorkflowOptions = {
|
|
150
125
|
/**
|
|
151
126
|
* Whether to stop execution immediately when a work fails.
|
|
152
127
|
* - true: Stop on first failure (default)
|
|
@@ -154,7 +129,7 @@ interface WorkflowOptions {
|
|
|
154
129
|
* @default true
|
|
155
130
|
*/
|
|
156
131
|
failFast?: boolean;
|
|
157
|
-
}
|
|
132
|
+
};
|
|
158
133
|
/**
|
|
159
134
|
* Helper type to extract work results from parallel works array.
|
|
160
135
|
* Since Work implements IWorkDefinition, we can use Extract directly.
|
|
@@ -243,10 +218,18 @@ declare function getWorkDefinition<TName extends string, TData, TResult, TAvaila
|
|
|
243
218
|
* ```
|
|
244
219
|
*/
|
|
245
220
|
declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = NonNullable<unknown>> implements IWorkflow<TData, TWorkResults> {
|
|
246
|
-
private
|
|
247
|
-
private
|
|
221
|
+
private _works;
|
|
222
|
+
private _options;
|
|
248
223
|
private _sealed;
|
|
249
224
|
constructor(options?: WorkflowOptions);
|
|
225
|
+
/**
|
|
226
|
+
* The list of works in the workflow (readonly)
|
|
227
|
+
*/
|
|
228
|
+
get works(): readonly WorkflowWork[];
|
|
229
|
+
/**
|
|
230
|
+
* The workflow options (readonly)
|
|
231
|
+
*/
|
|
232
|
+
get options(): Readonly<Required<WorkflowOptions>>;
|
|
250
233
|
/**
|
|
251
234
|
* Check if the workflow is sealed
|
|
252
235
|
*/
|
|
@@ -281,35 +264,24 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
281
264
|
*
|
|
282
265
|
* @example
|
|
283
266
|
* ```typescript
|
|
284
|
-
* // Without options - returns ISealedWorkflow
|
|
285
267
|
* const sealed = new Workflow<{ userId: string }>()
|
|
286
268
|
* .serial({ name: 'step1', execute: async () => 'result' })
|
|
287
269
|
* .seal();
|
|
288
270
|
*
|
|
271
|
+
* sealed.name; // 'seal'
|
|
289
272
|
* sealed.isSealed(); // true
|
|
290
273
|
* await sealed.run({ userId: '123' }); // OK
|
|
291
274
|
*
|
|
292
|
-
* //
|
|
293
|
-
*
|
|
294
|
-
* execute: async (ctx) => {
|
|
295
|
-
* console.log('Before:', ctx.data);
|
|
296
|
-
* const result = await workflow.run(ctx.data);
|
|
297
|
-
* console.log('After');
|
|
298
|
-
* return result;
|
|
299
|
-
* },
|
|
300
|
-
* // Optional: shouldRun, onError, silenceError (like IWorkDefinition)
|
|
301
|
-
* });
|
|
302
|
-
*
|
|
303
|
-
* sealedWithExecute.name; // 'seal'
|
|
304
|
-
* await sealedWithExecute.execute({ data: initialData, workResults: ... });
|
|
275
|
+
* // TypeScript prevents modifications:
|
|
276
|
+
* // sealed.serial(...) // Error: Property 'serial' does not exist
|
|
305
277
|
* ```
|
|
306
278
|
*/
|
|
307
279
|
seal(): ISealedWorkflow<TData, TWorkResults>;
|
|
308
|
-
seal(sealingWork: ISealingWorkDefinition<TData, TWorkResults>):
|
|
280
|
+
seal<TResult>(sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>): ISealedWorkflow<TData, TWorkResults>;
|
|
309
281
|
/**
|
|
310
282
|
* Execute the workflow with initial data
|
|
311
283
|
*/
|
|
312
|
-
run(initialData: TData): Promise<
|
|
284
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
313
285
|
/**
|
|
314
286
|
* Execute a single work
|
|
315
287
|
*/
|
|
@@ -320,4 +292,4 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
320
292
|
private executeParallelWorks;
|
|
321
293
|
}
|
|
322
294
|
|
|
323
|
-
export { type ISealedWorkflow, type
|
|
295
|
+
export { type ISealedWorkflow, type ISealingWorkDefinition, type IWorkDefinition, type IWorkResultsMap, type IWorkflow, type IWorkflowContext, type ParallelWorksToRecord, Work, type WorkInput, type WorkResult, type WorkStatus, Workflow, type WorkflowOptions, type WorkflowResult, type WorkflowStatus, type WorkflowWork, getWorkDefinition };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
// src/workflow.types.ts
|
|
2
|
-
var WorkStatus = /* @__PURE__ */ ((WorkStatus2) => {
|
|
3
|
-
WorkStatus2["PENDING"] = "pending";
|
|
4
|
-
WorkStatus2["RUNNING"] = "running";
|
|
5
|
-
WorkStatus2["COMPLETED"] = "completed";
|
|
6
|
-
WorkStatus2["FAILED"] = "failed";
|
|
7
|
-
WorkStatus2["SKIPPED"] = "skipped";
|
|
8
|
-
return WorkStatus2;
|
|
9
|
-
})(WorkStatus || {});
|
|
10
|
-
var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
|
|
11
|
-
WorkflowStatus2["PENDING"] = "pending";
|
|
12
|
-
WorkflowStatus2["RUNNING"] = "running";
|
|
13
|
-
WorkflowStatus2["COMPLETED"] = "completed";
|
|
14
|
-
WorkflowStatus2["FAILED"] = "failed";
|
|
15
|
-
return WorkflowStatus2;
|
|
16
|
-
})(WorkflowStatus || {});
|
|
17
|
-
|
|
18
1
|
// src/work.ts
|
|
19
2
|
var Work = class {
|
|
20
3
|
constructor(definition) {
|
|
@@ -52,9 +35,21 @@ var WorkResultsMap = class {
|
|
|
52
35
|
};
|
|
53
36
|
var Workflow = class {
|
|
54
37
|
constructor(options = {}) {
|
|
55
|
-
this.
|
|
38
|
+
this._works = [];
|
|
56
39
|
this._sealed = false;
|
|
57
|
-
this.
|
|
40
|
+
this._options = { failFast: true, ...options };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The list of works in the workflow (readonly)
|
|
44
|
+
*/
|
|
45
|
+
get works() {
|
|
46
|
+
return this._works;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The workflow options (readonly)
|
|
50
|
+
*/
|
|
51
|
+
get options() {
|
|
52
|
+
return this._options;
|
|
58
53
|
}
|
|
59
54
|
/**
|
|
60
55
|
* Check if the workflow is sealed
|
|
@@ -72,7 +67,7 @@ var Workflow = class {
|
|
|
72
67
|
if (this._sealed) {
|
|
73
68
|
throw new Error("Cannot add work to a sealed workflow");
|
|
74
69
|
}
|
|
75
|
-
this.
|
|
70
|
+
this._works.push({
|
|
76
71
|
type: "serial",
|
|
77
72
|
works: [getWorkDefinition(work)]
|
|
78
73
|
});
|
|
@@ -96,23 +91,21 @@ var Workflow = class {
|
|
|
96
91
|
if (this._sealed) {
|
|
97
92
|
throw new Error("Cannot add work to a sealed workflow");
|
|
98
93
|
}
|
|
99
|
-
this.
|
|
94
|
+
this._works.push({
|
|
100
95
|
type: "parallel",
|
|
101
96
|
works: works.map((w) => getWorkDefinition(w))
|
|
102
97
|
});
|
|
103
98
|
return this;
|
|
104
99
|
}
|
|
105
|
-
seal(
|
|
100
|
+
seal(_sealingWork) {
|
|
106
101
|
this._sealed = true;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
return this;
|
|
102
|
+
return {
|
|
103
|
+
name: "seal",
|
|
104
|
+
works: this._works,
|
|
105
|
+
options: this._options,
|
|
106
|
+
isSealed: () => this._sealed,
|
|
107
|
+
run: this.run.bind(this)
|
|
108
|
+
};
|
|
116
109
|
}
|
|
117
110
|
/**
|
|
118
111
|
* Execute the workflow with initial data
|
|
@@ -126,7 +119,7 @@ var Workflow = class {
|
|
|
126
119
|
const workResults = /* @__PURE__ */ new Map();
|
|
127
120
|
const collectedErrors = [];
|
|
128
121
|
try {
|
|
129
|
-
for (const workGroup of this.
|
|
122
|
+
for (const workGroup of this._works) {
|
|
130
123
|
try {
|
|
131
124
|
if (workGroup.type === "serial") {
|
|
132
125
|
await this.executeWork(workGroup.works[0], context, workResults);
|
|
@@ -135,7 +128,7 @@ var Workflow = class {
|
|
|
135
128
|
}
|
|
136
129
|
} catch (error) {
|
|
137
130
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
138
|
-
if (this.
|
|
131
|
+
if (this._options.failFast) {
|
|
139
132
|
throw err;
|
|
140
133
|
}
|
|
141
134
|
collectedErrors.push(err);
|
|
@@ -143,7 +136,7 @@ var Workflow = class {
|
|
|
143
136
|
}
|
|
144
137
|
if (collectedErrors.length > 0) {
|
|
145
138
|
return {
|
|
146
|
-
status: "failed"
|
|
139
|
+
status: "failed",
|
|
147
140
|
context,
|
|
148
141
|
workResults,
|
|
149
142
|
totalDuration: Date.now() - startTime,
|
|
@@ -151,14 +144,14 @@ var Workflow = class {
|
|
|
151
144
|
};
|
|
152
145
|
}
|
|
153
146
|
return {
|
|
154
|
-
status: "completed"
|
|
147
|
+
status: "completed",
|
|
155
148
|
context,
|
|
156
149
|
workResults,
|
|
157
150
|
totalDuration: Date.now() - startTime
|
|
158
151
|
};
|
|
159
152
|
} catch (error) {
|
|
160
153
|
return {
|
|
161
|
-
status: "failed"
|
|
154
|
+
status: "failed",
|
|
162
155
|
context,
|
|
163
156
|
workResults,
|
|
164
157
|
totalDuration: Date.now() - startTime,
|
|
@@ -175,7 +168,7 @@ var Workflow = class {
|
|
|
175
168
|
const shouldRun = await work.shouldRun(context);
|
|
176
169
|
if (!shouldRun) {
|
|
177
170
|
const skippedResult = {
|
|
178
|
-
status: "skipped"
|
|
171
|
+
status: "skipped",
|
|
179
172
|
duration: Date.now() - workStartTime
|
|
180
173
|
};
|
|
181
174
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -186,7 +179,7 @@ var Workflow = class {
|
|
|
186
179
|
try {
|
|
187
180
|
const result = await work.execute(context);
|
|
188
181
|
const workResult = {
|
|
189
|
-
status: "completed"
|
|
182
|
+
status: "completed",
|
|
190
183
|
result,
|
|
191
184
|
duration: Date.now() - workStartTime
|
|
192
185
|
};
|
|
@@ -195,7 +188,7 @@ var Workflow = class {
|
|
|
195
188
|
} catch (error) {
|
|
196
189
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
197
190
|
const failedResult = {
|
|
198
|
-
status: "failed"
|
|
191
|
+
status: "failed",
|
|
199
192
|
error: err,
|
|
200
193
|
duration: Date.now() - workStartTime
|
|
201
194
|
};
|
|
@@ -219,7 +212,7 @@ var Workflow = class {
|
|
|
219
212
|
const shouldRun = await work.shouldRun(context);
|
|
220
213
|
if (!shouldRun) {
|
|
221
214
|
const skippedResult = {
|
|
222
|
-
status: "skipped"
|
|
215
|
+
status: "skipped",
|
|
223
216
|
duration: Date.now() - workStartTime
|
|
224
217
|
};
|
|
225
218
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -244,7 +237,7 @@ var Workflow = class {
|
|
|
244
237
|
const duration = Date.now() - result.startTime;
|
|
245
238
|
if ("error" in result && result.error) {
|
|
246
239
|
const workResult = {
|
|
247
|
-
status: "failed"
|
|
240
|
+
status: "failed",
|
|
248
241
|
error: result.error,
|
|
249
242
|
duration
|
|
250
243
|
};
|
|
@@ -255,7 +248,7 @@ var Workflow = class {
|
|
|
255
248
|
}
|
|
256
249
|
} else {
|
|
257
250
|
const workResult = {
|
|
258
|
-
status: "completed"
|
|
251
|
+
status: "completed",
|
|
259
252
|
result: result.result,
|
|
260
253
|
duration
|
|
261
254
|
};
|
|
@@ -275,9 +268,7 @@ var Workflow = class {
|
|
|
275
268
|
};
|
|
276
269
|
export {
|
|
277
270
|
Work,
|
|
278
|
-
WorkStatus,
|
|
279
271
|
Workflow,
|
|
280
|
-
WorkflowStatus,
|
|
281
272
|
getWorkDefinition
|
|
282
273
|
};
|
|
283
274
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 a work result with compile-time type checking */\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 /** Check if a work result exists */\n has(name: string): 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 /** Optional: if true, errors won't stop the workflow (result will be undefined) */\n silenceError?: boolean;\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\n/**\n * Interface for the Workflow class.\n * Defines all methods available on a workflow before sealing.\n */\nexport interface IWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Add a serial work to the workflow\n */\n serial<TName extends string, TResult>(\n work: IWorkDefinition<TName, TData, TResult, TWorkResults>\n ): IWorkflow<TData, TWorkResults & { [K in TName]: TResult }>;\n\n /**\n * Add parallel works to the workflow\n */\n\n parallel(\n works: readonly IWorkDefinition<string, TData, any, TWorkResults>[]\n ): IWorkflow<TData, any>;\n\n /**\n * Seal the workflow to prevent further modifications\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults>\n ): ISealedWorkflowWithExecute<TData, TWorkResults>;\n\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean;\n\n /**\n * Execute the workflow with initial data\n */\n run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;\n}\n\n/**\n * A limited work definition for sealing a workflow.\n * Similar to IWorkDefinition but without 'name' (hardcoded as 'seal').\n * The execute function returns IWorkflowResult instead of a custom result type.\n */\nexport type ISealingWorkDefinition<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> = Omit<\n IWorkDefinition<'seal', TData, IWorkflowResult<TData, TWorkResults>, TWorkResults>,\n 'name'\n>;\n\n/**\n * A sealed workflow that can only be executed, not modified.\n * Use workflow.seal() to create a sealed workflow.\n */\nexport interface ISealedWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean;\n\n /**\n * Execute the workflow with initial data\n */\n run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;\n}\n\n/**\n * A sealed workflow with a custom execute function.\n * Created when seal() is called with an execute option.\n */\nexport interface ISealedWorkflowWithExecute<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> extends ISealedWorkflow<TData, TWorkResults> {\n /**\n * Hardcoded name for the sealed workflow\n */\n readonly name: 'seal';\n\n /**\n * Custom execute function provided during sealing.\n * Receives context (like a Work).\n */\n execute(\n context: IWorkflowContext<TData, TWorkResults>\n ): Promise<IWorkflowResult<TData, TWorkResults>>;\n}\n\n/**\n * Options for configuring workflow behavior\n */\nexport interface WorkflowOptions {\n /**\n * Whether to stop execution immediately when a work fails.\n * - true: Stop on first failure (default)\n * - false: Continue executing remaining works, fail at the end if any work failed\n * @default true\n */\n failFast?: boolean;\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 */\nexport type 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","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 /** Optional: if true, errors won't stop the workflow (result will be undefined) */\n readonly silenceError?: boolean;\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 this.silenceError = definition.silenceError;\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 IWorkflow,\n ISealedWorkflow,\n ISealingWorkDefinition,\n ISealedWorkflowWithExecute,\n WorkflowOptions,\n ParallelWorksToRecord,\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<K extends keyof TWorkResults>(name: K): 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> implements IWorkflow<TData, TWorkResults> {\n private works: IWorkflowWork[] = [];\n private options: Required<WorkflowOptions>;\n private _sealed = false;\n\n constructor(options: WorkflowOptions = {}) {\n this.options = { failFast: true, ...options };\n }\n\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean {\n return this._sealed;\n }\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 * @throws Error if the workflow is sealed\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\n * @throws Error if the workflow is sealed\n *\n * @example\n * ```typescript\n * workflow.parallel([\n * { name: 'work1', execute: async () => 'result1' },\n * { name: 'work2', execute: async () => 123 },\n * ]);\n * ```\n */\n parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(\n works: TParallelWorks\n ): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)),\n });\n return this as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Seal the workflow to prevent further modifications.\n * Returns a SealedWorkflow that can only be executed with run().\n *\n * @example\n * ```typescript\n * // Without options - returns ISealedWorkflow\n * const sealed = new Workflow<{ userId: string }>()\n * .serial({ name: 'step1', execute: async () => 'result' })\n * .seal();\n *\n * sealed.isSealed(); // true\n * await sealed.run({ userId: '123' }); // OK\n *\n * // With sealing work - returns ISealedWorkflowWithExecute\n * const sealedWithExecute = workflow.seal({\n * execute: async (ctx) => {\n * console.log('Before:', ctx.data);\n * const result = await workflow.run(ctx.data);\n * console.log('After');\n * return result;\n * },\n * // Optional: shouldRun, onError, silenceError (like IWorkDefinition)\n * });\n *\n * sealedWithExecute.name; // 'seal'\n * await sealedWithExecute.execute({ data: initialData, workResults: ... });\n * ```\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults>\n ): ISealedWorkflowWithExecute<TData, TWorkResults>;\n seal(\n sealingWork?: ISealingWorkDefinition<TData, TWorkResults>\n ): ISealedWorkflow<TData, TWorkResults> | ISealedWorkflowWithExecute<TData, TWorkResults> {\n this._sealed = true;\n if (sealingWork?.execute) {\n return {\n name: 'seal',\n isSealed: () => this._sealed,\n run: this.run.bind(this),\n execute: sealingWork.execute,\n };\n }\n return this;\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 const collectedErrors: Error[] = [];\n\n try {\n for (const workGroup of this.works) {\n try {\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 } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n if (this.options.failFast) {\n throw err;\n }\n collectedErrors.push(err);\n }\n }\n\n // If failFast is false, check for collected errors\n if (collectedErrors.length > 0) {\n return {\n status: WorkflowStatus.FAILED,\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: collectedErrors[0],\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 const failedResult: IWorkResult = {\n status: WorkStatus.FAILED,\n error: err,\n duration: Date.now() - workStartTime,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, failedResult as any);\n workResults.set(work.name as keyof TWorkResults, failedResult);\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 (unless silenceError is true)\n if (!work.silenceError) {\n throw err;\n }\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 // 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 // Only track as error if silenceError is not set\n if (!result.work.silenceError) {\n errors.push({ work: result.work, error: result.error });\n }\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 // Call error handlers for all failed works (including silenced ones)\n for (const result of results) {\n if ('error' in result && result.error && result.work.onError) {\n await result.work.onError(result.error, context);\n }\n }\n\n // Throw the first non-silenced error to stop workflow\n if (errors.length > 0) {\n throw errors[0].error;\n }\n }\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,EAqBzE,YAAY,YAA2E;AACrF,SAAK,OAAO,WAAW;AACvB,SAAK,UAAU,WAAW;AAC1B,SAAK,YAAY,WAAW;AAC5B,SAAK,UAAU,WAAW;AAC1B,SAAK,eAAe,WAAW;AAAA,EACjC;AACF;AAiBO,SAAS,kBAMd,OAC+D;AAC/D,SAAO;AACT;;;AC1DA,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,IAAkC,MAAkB;AAClD,WAAO,KAAK,IAAI,IAAI,IAAI;AAAA,EAC1B;AACF;AAqCO,IAAM,WAAN,MAGqC;AAAA,EAK1C,YAAY,UAA2B,CAAC,GAAG;AAJ3C,SAAQ,QAAyB,CAAC;AAElC,SAAQ,UAAU;AAGhB,SAAK,UAAU,EAAE,UAAU,MAAM,GAAG,QAAQ;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OACE,MAC2D;AAC3D,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,SACE,OACuE;AACvE,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AACA,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAC9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAmCA,KACE,aACwF;AACxF,SAAK,UAAU;AACf,QAAI,aAAa,SAAS;AACxB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,MAAM,KAAK;AAAA,QACrB,KAAK,KAAK,IAAI,KAAK,IAAI;AAAA,QACvB,SAAS,YAAY;AAAA,MACvB;AAAA,IACF;AACA,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;AAC7D,UAAM,kBAA2B,CAAC;AAElC,QAAI;AACF,iBAAW,aAAa,KAAK,OAAO;AAClC,YAAI;AACF,cAAI,UAAU,SAAS,UAAU;AAC/B,kBAAM,KAAK,YAAY,UAAU,MAAM,CAAC,GAAG,SAAS,WAAW;AAAA,UACjE,OAAO;AACL,kBAAM,KAAK,qBAAqB,UAAU,OAAO,SAAS,WAAW;AAAA,UACvE;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,cAAI,KAAK,QAAQ,UAAU;AACzB,kBAAM;AAAA,UACR;AACA,0BAAgB,KAAK,GAAG;AAAA,QAC1B;AAAA,MACF;AAGA,UAAI,gBAAgB,SAAS,GAAG;AAC9B,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC5B,OAAO,gBAAgB,CAAC;AAAA,QAC1B;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,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,QACP,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAGA,cAAQ,YAAY,IAAI,KAAK,MAA4B,YAAmB;AAC5E,kBAAY,IAAI,KAAK,MAA4B,YAAY;AAG7D,UAAI,KAAK,SAAS;AAChB,cAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,MACjC;AAGA,UAAI,CAAC,KAAK,cAAc;AACtB,cAAM;AAAA,MACR;AAAA,IACF;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;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAElE,YAAI,CAAC,OAAO,KAAK,cAAc;AAC7B,iBAAO,KAAK,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,QACxD;AAAA,MACF,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,eAAW,UAAU,SAAS;AAC5B,UAAI,WAAW,UAAU,OAAO,SAAS,OAAO,KAAK,SAAS;AAC5D,cAAM,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO;AAAA,MACjD;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,OAAO,CAAC,EAAE;AAAA,IAClB;AAAA,EACF;AACF;","names":["WorkStatus","WorkflowStatus"]}
|
|
1
|
+
{"version":3,"sources":["../src/work.ts","../src/workflow.ts"],"sourcesContent":["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 /** Optional: if true, errors won't stop the workflow (result will be undefined) */\n readonly silenceError?: boolean;\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 this.silenceError = definition.silenceError;\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 WorkflowResult,\n IWorkResultsMap,\n WorkflowWork,\n IWorkDefinition,\n WorkResult,\n IWorkflow,\n ISealedWorkflow,\n ISealingWorkDefinition,\n WorkflowOptions,\n ParallelWorksToRecord,\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, WorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): WorkResult<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 WorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: WorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has<K extends keyof TWorkResults>(name: K): 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> implements IWorkflow<TData, TWorkResults> {\n private _works: WorkflowWork[] = [];\n private _options: Required<WorkflowOptions>;\n private _sealed = false;\n\n constructor(options: WorkflowOptions = {}) {\n this._options = { failFast: true, ...options };\n }\n\n /**\n * The list of works in the workflow (readonly)\n */\n get works(): readonly WorkflowWork[] {\n return this._works;\n }\n\n /**\n * The workflow options (readonly)\n */\n get options(): Readonly<Required<WorkflowOptions>> {\n return this._options;\n }\n\n /**\n * Check if the workflow is sealed\n */\n isSealed(): boolean {\n return this._sealed;\n }\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 * @throws Error if the workflow is sealed\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this._works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\n * @throws Error if the workflow is sealed\n *\n * @example\n * ```typescript\n * workflow.parallel([\n * { name: 'work1', execute: async () => 'result1' },\n * { name: 'work2', execute: async () => 123 },\n * ]);\n * ```\n */\n parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(\n works: TParallelWorks\n ): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>> {\n if (this._sealed) {\n throw new Error('Cannot add work to a sealed workflow');\n }\n this._works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)),\n });\n return this as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Seal the workflow to prevent further modifications.\n * Returns a SealedWorkflow that can only be executed with run().\n *\n * @example\n * ```typescript\n * const sealed = new Workflow<{ userId: string }>()\n * .serial({ name: 'step1', execute: async () => 'result' })\n * .seal();\n *\n * sealed.name; // 'seal'\n * sealed.isSealed(); // true\n * await sealed.run({ userId: '123' }); // OK\n *\n * // TypeScript prevents modifications:\n * // sealed.serial(...) // Error: Property 'serial' does not exist\n * ```\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal<TResult>(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>\n ): ISealedWorkflow<TData, TWorkResults>;\n seal<TResult>(\n _sealingWork?: ISealingWorkDefinition<TData, TWorkResults, TResult>\n ): ISealedWorkflow<TData, TWorkResults> {\n this._sealed = true;\n return {\n name: 'seal',\n works: this._works,\n options: this._options,\n isSealed: () => this._sealed,\n run: this.run.bind(this),\n };\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<WorkflowResult<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, WorkResult>();\n const collectedErrors: Error[] = [];\n\n try {\n for (const workGroup of this._works) {\n try {\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 } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n if (this._options.failFast) {\n throw err;\n }\n collectedErrors.push(err);\n }\n }\n\n // If failFast is false, check for collected errors\n if (collectedErrors.length > 0) {\n return {\n status: 'failed',\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n error: collectedErrors[0],\n };\n }\n\n return {\n status: 'completed',\n context,\n workResults,\n totalDuration: Date.now() - startTime,\n };\n } catch (error) {\n return {\n status: '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, WorkResult>\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: WorkResult = {\n status: '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: WorkResult = {\n status: '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 const failedResult: WorkResult = {\n status: 'failed',\n error: err,\n duration: Date.now() - workStartTime,\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n context.workResults.set(work.name as keyof TWorkResults, failedResult as any);\n workResults.set(work.name as keyof TWorkResults, failedResult);\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 (unless silenceError is true)\n if (!work.silenceError) {\n throw err;\n }\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, WorkResult>\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: WorkResult = {\n status: '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: WorkResult = {\n status: 'failed',\n error: result.error,\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 // Only track as error if silenceError is not set\n if (!result.work.silenceError) {\n errors.push({ work: result.work, error: result.error });\n }\n } else {\n const workResult: WorkResult = {\n status: '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 // Call error handlers for all failed works (including silenced ones)\n for (const result of results) {\n if ('error' in result && result.error && result.work.onError) {\n await result.work.onError(result.error, context);\n }\n }\n\n // Throw the first non-silenced error to stop workflow\n if (errors.length > 0) {\n throw errors[0].error;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yigitahmetsahin/workflow-ts",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.3",
|
|
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",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"test:watch": "vitest",
|
|
23
23
|
"test:coverage": "vitest run --coverage",
|
|
24
24
|
"format": "prettier --write .",
|
|
25
|
-
"lint": "npm run format && eslint src examples --fix && tsc --noEmit",
|
|
26
|
-
"lint:check": "prettier --check . && eslint src examples && tsc --noEmit",
|
|
25
|
+
"lint": "npm run format && eslint src examples --fix && tsc --noEmit -p tsconfig.check.json",
|
|
26
|
+
"lint:check": "prettier --check . && eslint src examples && tsc --noEmit -p tsconfig.check.json",
|
|
27
27
|
"prepublishOnly": "npm run build"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"homepage": "https://github.com/yigitahmetsahin/workflow-ts#readme",
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@eslint/js": "^9.39.2",
|
|
51
|
+
"@types/node": "^25.0.10",
|
|
52
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
51
53
|
"eslint": "^9.39.2",
|
|
52
54
|
"eslint-config-prettier": "^10.1.8",
|
|
53
55
|
"prettier": "^3.8.1",
|