@yigitahmetsahin/workflow-ts 3.4.2 → 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 +15 -15
- package/dist/index.cjs +9 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -29
- package/dist/index.d.ts +18 -29
- package/dist/index.js +9 -28
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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
|
}
|
|
@@ -256,7 +256,7 @@ const workflow = new Workflow<{ userId: string }>()
|
|
|
256
256
|
execute: async (ctx) => {
|
|
257
257
|
// Check if previous work failed
|
|
258
258
|
const optionalResult = ctx.workResults.get('fetchOptionalData');
|
|
259
|
-
if (optionalResult.status ===
|
|
259
|
+
if (optionalResult.status === 'failed') {
|
|
260
260
|
return { data: null, error: optionalResult.error?.message };
|
|
261
261
|
}
|
|
262
262
|
return { data: optionalResult.result };
|
|
@@ -264,7 +264,7 @@ const workflow = new Workflow<{ userId: string }>()
|
|
|
264
264
|
});
|
|
265
265
|
|
|
266
266
|
const result = await workflow.run({ userId: '123' });
|
|
267
|
-
// result.status ===
|
|
267
|
+
// result.status === 'completed' (workflow continues despite error)
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
### Workflow Options
|
|
@@ -307,7 +307,7 @@ const workflow = new Workflow<{ userId: string }>({ failFast: false })
|
|
|
307
307
|
// work3 WILL execute, but workflow still fails at the end
|
|
308
308
|
|
|
309
309
|
const result = await workflow.run({ userId: '123' });
|
|
310
|
-
// result.status ===
|
|
310
|
+
// result.status === 'failed'
|
|
311
311
|
// result.error.message === 'Continue anyway' (first error)
|
|
312
312
|
// result.context.workResults.get('work3')?.result === 'still runs'
|
|
313
313
|
```
|
|
@@ -333,7 +333,7 @@ const workflow = new Workflow<{ userId: string }>({ failFast: false })
|
|
|
333
333
|
.serial({ name: 'final', execute: async () => 'done' });
|
|
334
334
|
|
|
335
335
|
const result = await workflow.run({ userId: '123' });
|
|
336
|
-
// result.status ===
|
|
336
|
+
// result.status === 'completed' (all errors silenced)
|
|
337
337
|
```
|
|
338
338
|
|
|
339
339
|
### `.run(initialData)`
|
|
@@ -347,29 +347,29 @@ const result = await workflow.run({ userId: '123' });
|
|
|
347
347
|
### Result Object
|
|
348
348
|
|
|
349
349
|
```typescript
|
|
350
|
-
|
|
350
|
+
type WorkflowResult = {
|
|
351
351
|
status: WorkflowStatus; // 'completed' | 'failed'
|
|
352
352
|
context: {
|
|
353
353
|
data: TData; // Initial data passed to run()
|
|
354
354
|
workResults: IWorkResultsMap; // Type-safe map of work results
|
|
355
355
|
};
|
|
356
|
-
workResults: Map<string,
|
|
356
|
+
workResults: Map<string, WorkResult>; // Detailed results per work
|
|
357
357
|
totalDuration: number; // Total execution time in ms
|
|
358
358
|
error?: Error; // Error if workflow failed
|
|
359
|
-
}
|
|
359
|
+
};
|
|
360
360
|
|
|
361
361
|
// Each work result contains execution details
|
|
362
|
-
|
|
362
|
+
type WorkResult<T> = {
|
|
363
363
|
status: WorkStatus; // 'completed' | 'failed' | 'skipped'
|
|
364
364
|
result?: T; // The return value from execute()
|
|
365
365
|
error?: Error; // Error if work failed
|
|
366
366
|
duration: number; // Execution time in ms
|
|
367
|
-
}
|
|
367
|
+
};
|
|
368
368
|
```
|
|
369
369
|
|
|
370
370
|
### Accessing Work Results
|
|
371
371
|
|
|
372
|
-
`ctx.workResults.get()` returns
|
|
372
|
+
`ctx.workResults.get()` returns a `WorkResult` object, not the raw value:
|
|
373
373
|
|
|
374
374
|
```typescript
|
|
375
375
|
// Get the full work result with metadata
|
|
@@ -381,7 +381,7 @@ console.log(workResult.duration); // execution time in ms
|
|
|
381
381
|
const user = ctx.workResults.get('fetchUser').result;
|
|
382
382
|
|
|
383
383
|
// Check status before accessing result
|
|
384
|
-
if (workResult.status ===
|
|
384
|
+
if (workResult.status === 'completed') {
|
|
385
385
|
console.log('User:', workResult.result);
|
|
386
386
|
}
|
|
387
387
|
```
|
|
@@ -407,9 +407,9 @@ Skipped works are still accessible via `workResults.get()` with `status: 'skippe
|
|
|
407
407
|
|
|
408
408
|
```typescript
|
|
409
409
|
const emailResult = ctx.workResults.get('sendEmail');
|
|
410
|
-
if (emailResult.status ===
|
|
410
|
+
if (emailResult.status === 'skipped') {
|
|
411
411
|
console.log('Email was skipped');
|
|
412
|
-
} else if (emailResult.status ===
|
|
412
|
+
} else if (emailResult.status === 'completed') {
|
|
413
413
|
console.log('Email sent:', emailResult.result);
|
|
414
414
|
}
|
|
415
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) {
|
|
@@ -183,7 +164,7 @@ var Workflow = class {
|
|
|
183
164
|
}
|
|
184
165
|
if (collectedErrors.length > 0) {
|
|
185
166
|
return {
|
|
186
|
-
status: "failed"
|
|
167
|
+
status: "failed",
|
|
187
168
|
context,
|
|
188
169
|
workResults,
|
|
189
170
|
totalDuration: Date.now() - startTime,
|
|
@@ -191,14 +172,14 @@ var Workflow = class {
|
|
|
191
172
|
};
|
|
192
173
|
}
|
|
193
174
|
return {
|
|
194
|
-
status: "completed"
|
|
175
|
+
status: "completed",
|
|
195
176
|
context,
|
|
196
177
|
workResults,
|
|
197
178
|
totalDuration: Date.now() - startTime
|
|
198
179
|
};
|
|
199
180
|
} catch (error) {
|
|
200
181
|
return {
|
|
201
|
-
status: "failed"
|
|
182
|
+
status: "failed",
|
|
202
183
|
context,
|
|
203
184
|
workResults,
|
|
204
185
|
totalDuration: Date.now() - startTime,
|
|
@@ -215,7 +196,7 @@ var Workflow = class {
|
|
|
215
196
|
const shouldRun = await work.shouldRun(context);
|
|
216
197
|
if (!shouldRun) {
|
|
217
198
|
const skippedResult = {
|
|
218
|
-
status: "skipped"
|
|
199
|
+
status: "skipped",
|
|
219
200
|
duration: Date.now() - workStartTime
|
|
220
201
|
};
|
|
221
202
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -226,7 +207,7 @@ var Workflow = class {
|
|
|
226
207
|
try {
|
|
227
208
|
const result = await work.execute(context);
|
|
228
209
|
const workResult = {
|
|
229
|
-
status: "completed"
|
|
210
|
+
status: "completed",
|
|
230
211
|
result,
|
|
231
212
|
duration: Date.now() - workStartTime
|
|
232
213
|
};
|
|
@@ -235,7 +216,7 @@ var Workflow = class {
|
|
|
235
216
|
} catch (error) {
|
|
236
217
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
237
218
|
const failedResult = {
|
|
238
|
-
status: "failed"
|
|
219
|
+
status: "failed",
|
|
239
220
|
error: err,
|
|
240
221
|
duration: Date.now() - workStartTime
|
|
241
222
|
};
|
|
@@ -259,7 +240,7 @@ var Workflow = class {
|
|
|
259
240
|
const shouldRun = await work.shouldRun(context);
|
|
260
241
|
if (!shouldRun) {
|
|
261
242
|
const skippedResult = {
|
|
262
|
-
status: "skipped"
|
|
243
|
+
status: "skipped",
|
|
263
244
|
duration: Date.now() - workStartTime
|
|
264
245
|
};
|
|
265
246
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -284,7 +265,7 @@ var Workflow = class {
|
|
|
284
265
|
const duration = Date.now() - result.startTime;
|
|
285
266
|
if ("error" in result && result.error) {
|
|
286
267
|
const workResult = {
|
|
287
|
-
status: "failed"
|
|
268
|
+
status: "failed",
|
|
288
269
|
error: result.error,
|
|
289
270
|
duration
|
|
290
271
|
};
|
|
@@ -295,7 +276,7 @@ var Workflow = class {
|
|
|
295
276
|
}
|
|
296
277
|
} else {
|
|
297
278
|
const workResult = {
|
|
298
|
-
status: "completed"
|
|
279
|
+
status: "completed",
|
|
299
280
|
result: result.result,
|
|
300
281
|
duration
|
|
301
282
|
};
|
|
@@ -316,9 +297,7 @@ var Workflow = class {
|
|
|
316
297
|
// Annotate the CommonJS export names for ESM import in node:
|
|
317
298
|
0 && (module.exports = {
|
|
318
299
|
Work,
|
|
319
|
-
WorkStatus,
|
|
320
300
|
Workflow,
|
|
321
|
-
WorkflowStatus,
|
|
322
301
|
getWorkDefinition
|
|
323
302
|
});
|
|
324
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 * The list of works in the workflow (readonly)\n */\n readonly works: readonly IWorkflowWork[];\n\n /**\n * The workflow options (readonly)\n */\n readonly options: Readonly<Required<WorkflowOptions>>;\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parallel(works: readonly IWorkDefinition<string, TData, any, TWorkResults>[]): IWorkflow<\n TData,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >;\n\n /**\n * Seal the workflow to prevent further modifications\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal<TResult>(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>\n ): ISealedWorkflow<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 work definition for sealing a workflow.\n * Similar to IWorkDefinition but without 'name'.\n */\nexport type ISealingWorkDefinition<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n TResult = unknown,\n> = Omit<IWorkDefinition<'seal', TData, TResult, TWorkResults>, 'name'>;\n\n/**\n * A sealed workflow that can only be executed, not modified.\n * Use workflow.seal() to create a sealed workflow.\n * Picks `works`, `options`, `isSealed`, and `run` from IWorkflow, adds `name: 'seal'`.\n */\nexport type ISealedWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> = Pick<IWorkflow<TData, TWorkResults>, 'works' | 'options' | 'isSealed' | 'run'> & {\n readonly name: 'seal';\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 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 * The list of works in the workflow (readonly)\n */\n get works(): readonly IWorkflowWork[] {\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<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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACoBO,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;;;AC7DA,IAAM,iBAAN,MAE2C;AAAA,EAF3C;AAGE,SAAQ,MAAM,oBAAI,IAA6C;AAAA;AAAA,EAE/D,IAAkC,MAAsC;AACtE,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,OAA0C;AACnF,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,SAAyB,CAAC;AAElC,SAAQ,UAAU;AAGhB,SAAK,WAAW,EAAE,UAAU,MAAM,GAAG,QAAQ;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAA+C;AACjD,WAAO,KAAK;AAAA,EACd;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,OAAO,KAAK;AAAA,MACf,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,OAAO,KAAK;AAAA,MACf,MAAM;AAAA,MACN,OAAO,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAC9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAwBA,KACE,cACsC;AACtC,SAAK,UAAU;AACf,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK;AAAA,MACd,UAAU,MAAM,KAAK;AAAA,MACrB,KAAK,KAAK,IAAI,KAAK,IAAI;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,aAAkE;AAC1E,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,UAAiD;AAAA,MACrD,MAAM;AAAA,MACN,aAAa,IAAI,eAA6B;AAAA,IAChD;AACA,UAAM,cAAc,oBAAI,IAAoC;AAC5D,UAAM,kBAA2B,CAAC;AAElC,QAAI;AACF,iBAAW,aAAa,KAAK,QAAQ;AACnC,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,SAAS,UAAU;AAC1B,kBAAM;AAAA,UACR;AACA,0BAAgB,KAAK,GAAG;AAAA,QAC1B;AAAA,MACF;AAGA,UAAI,gBAAgB,SAAS,GAAG;AAC9B,eAAO;AAAA,UACL,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC5B,OAAO,gBAAgB,CAAC;AAAA,QAC1B;AAAA,MACF;AAEA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;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,gBAA4B;AAAA,UAChC,QAAQ;AAAA,UACR,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,aAAyB;AAAA,QAC7B,QAAQ;AAAA,QACR;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,eAA2B;AAAA,QAC/B,QAAQ;AAAA,QACR,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,gBAA4B;AAAA,YAChC,QAAQ;AAAA,YACR,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,aAAyB;AAAA,UAC7B,QAAQ;AAAA,UACR,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,aAAyB;AAAA,UAC7B,QAAQ;AAAA,UACR,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":[]}
|
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,20 +54,20 @@ 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.
|
|
@@ -87,7 +76,7 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
87
76
|
/**
|
|
88
77
|
* The list of works in the workflow (readonly)
|
|
89
78
|
*/
|
|
90
|
-
readonly works: readonly
|
|
79
|
+
readonly works: readonly WorkflowWork[];
|
|
91
80
|
/**
|
|
92
81
|
* The workflow options (readonly)
|
|
93
82
|
*/
|
|
@@ -114,7 +103,7 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
114
103
|
/**
|
|
115
104
|
* Execute the workflow with initial data
|
|
116
105
|
*/
|
|
117
|
-
run(initialData: TData): Promise<
|
|
106
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
118
107
|
}
|
|
119
108
|
/**
|
|
120
109
|
* A work definition for sealing a workflow.
|
|
@@ -132,7 +121,7 @@ type ISealedWorkflow<TData = Record<string, unknown>, TWorkResults extends Recor
|
|
|
132
121
|
/**
|
|
133
122
|
* Options for configuring workflow behavior
|
|
134
123
|
*/
|
|
135
|
-
|
|
124
|
+
type WorkflowOptions = {
|
|
136
125
|
/**
|
|
137
126
|
* Whether to stop execution immediately when a work fails.
|
|
138
127
|
* - true: Stop on first failure (default)
|
|
@@ -140,7 +129,7 @@ interface WorkflowOptions {
|
|
|
140
129
|
* @default true
|
|
141
130
|
*/
|
|
142
131
|
failFast?: boolean;
|
|
143
|
-
}
|
|
132
|
+
};
|
|
144
133
|
/**
|
|
145
134
|
* Helper type to extract work results from parallel works array.
|
|
146
135
|
* Since Work implements IWorkDefinition, we can use Extract directly.
|
|
@@ -236,7 +225,7 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
236
225
|
/**
|
|
237
226
|
* The list of works in the workflow (readonly)
|
|
238
227
|
*/
|
|
239
|
-
get works(): readonly
|
|
228
|
+
get works(): readonly WorkflowWork[];
|
|
240
229
|
/**
|
|
241
230
|
* The workflow options (readonly)
|
|
242
231
|
*/
|
|
@@ -292,7 +281,7 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
292
281
|
/**
|
|
293
282
|
* Execute the workflow with initial data
|
|
294
283
|
*/
|
|
295
|
-
run(initialData: TData): Promise<
|
|
284
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
296
285
|
/**
|
|
297
286
|
* Execute a single work
|
|
298
287
|
*/
|
|
@@ -303,4 +292,4 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
303
292
|
private executeParallelWorks;
|
|
304
293
|
}
|
|
305
294
|
|
|
306
|
-
export { type ISealedWorkflow, type ISealingWorkDefinition, type IWorkDefinition, 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,20 +54,20 @@ 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.
|
|
@@ -87,7 +76,7 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
87
76
|
/**
|
|
88
77
|
* The list of works in the workflow (readonly)
|
|
89
78
|
*/
|
|
90
|
-
readonly works: readonly
|
|
79
|
+
readonly works: readonly WorkflowWork[];
|
|
91
80
|
/**
|
|
92
81
|
* The workflow options (readonly)
|
|
93
82
|
*/
|
|
@@ -114,7 +103,7 @@ interface IWorkflow<TData = Record<string, unknown>, TWorkResults extends Record
|
|
|
114
103
|
/**
|
|
115
104
|
* Execute the workflow with initial data
|
|
116
105
|
*/
|
|
117
|
-
run(initialData: TData): Promise<
|
|
106
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
118
107
|
}
|
|
119
108
|
/**
|
|
120
109
|
* A work definition for sealing a workflow.
|
|
@@ -132,7 +121,7 @@ type ISealedWorkflow<TData = Record<string, unknown>, TWorkResults extends Recor
|
|
|
132
121
|
/**
|
|
133
122
|
* Options for configuring workflow behavior
|
|
134
123
|
*/
|
|
135
|
-
|
|
124
|
+
type WorkflowOptions = {
|
|
136
125
|
/**
|
|
137
126
|
* Whether to stop execution immediately when a work fails.
|
|
138
127
|
* - true: Stop on first failure (default)
|
|
@@ -140,7 +129,7 @@ interface WorkflowOptions {
|
|
|
140
129
|
* @default true
|
|
141
130
|
*/
|
|
142
131
|
failFast?: boolean;
|
|
143
|
-
}
|
|
132
|
+
};
|
|
144
133
|
/**
|
|
145
134
|
* Helper type to extract work results from parallel works array.
|
|
146
135
|
* Since Work implements IWorkDefinition, we can use Extract directly.
|
|
@@ -236,7 +225,7 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
236
225
|
/**
|
|
237
226
|
* The list of works in the workflow (readonly)
|
|
238
227
|
*/
|
|
239
|
-
get works(): readonly
|
|
228
|
+
get works(): readonly WorkflowWork[];
|
|
240
229
|
/**
|
|
241
230
|
* The workflow options (readonly)
|
|
242
231
|
*/
|
|
@@ -292,7 +281,7 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
292
281
|
/**
|
|
293
282
|
* Execute the workflow with initial data
|
|
294
283
|
*/
|
|
295
|
-
run(initialData: TData): Promise<
|
|
284
|
+
run(initialData: TData): Promise<WorkflowResult<TData, TWorkResults>>;
|
|
296
285
|
/**
|
|
297
286
|
* Execute a single work
|
|
298
287
|
*/
|
|
@@ -303,4 +292,4 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
|
|
|
303
292
|
private executeParallelWorks;
|
|
304
293
|
}
|
|
305
294
|
|
|
306
|
-
export { type ISealedWorkflow, type ISealingWorkDefinition, type IWorkDefinition, 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) {
|
|
@@ -153,7 +136,7 @@ var Workflow = class {
|
|
|
153
136
|
}
|
|
154
137
|
if (collectedErrors.length > 0) {
|
|
155
138
|
return {
|
|
156
|
-
status: "failed"
|
|
139
|
+
status: "failed",
|
|
157
140
|
context,
|
|
158
141
|
workResults,
|
|
159
142
|
totalDuration: Date.now() - startTime,
|
|
@@ -161,14 +144,14 @@ var Workflow = class {
|
|
|
161
144
|
};
|
|
162
145
|
}
|
|
163
146
|
return {
|
|
164
|
-
status: "completed"
|
|
147
|
+
status: "completed",
|
|
165
148
|
context,
|
|
166
149
|
workResults,
|
|
167
150
|
totalDuration: Date.now() - startTime
|
|
168
151
|
};
|
|
169
152
|
} catch (error) {
|
|
170
153
|
return {
|
|
171
|
-
status: "failed"
|
|
154
|
+
status: "failed",
|
|
172
155
|
context,
|
|
173
156
|
workResults,
|
|
174
157
|
totalDuration: Date.now() - startTime,
|
|
@@ -185,7 +168,7 @@ var Workflow = class {
|
|
|
185
168
|
const shouldRun = await work.shouldRun(context);
|
|
186
169
|
if (!shouldRun) {
|
|
187
170
|
const skippedResult = {
|
|
188
|
-
status: "skipped"
|
|
171
|
+
status: "skipped",
|
|
189
172
|
duration: Date.now() - workStartTime
|
|
190
173
|
};
|
|
191
174
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -196,7 +179,7 @@ var Workflow = class {
|
|
|
196
179
|
try {
|
|
197
180
|
const result = await work.execute(context);
|
|
198
181
|
const workResult = {
|
|
199
|
-
status: "completed"
|
|
182
|
+
status: "completed",
|
|
200
183
|
result,
|
|
201
184
|
duration: Date.now() - workStartTime
|
|
202
185
|
};
|
|
@@ -205,7 +188,7 @@ var Workflow = class {
|
|
|
205
188
|
} catch (error) {
|
|
206
189
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
207
190
|
const failedResult = {
|
|
208
|
-
status: "failed"
|
|
191
|
+
status: "failed",
|
|
209
192
|
error: err,
|
|
210
193
|
duration: Date.now() - workStartTime
|
|
211
194
|
};
|
|
@@ -229,7 +212,7 @@ var Workflow = class {
|
|
|
229
212
|
const shouldRun = await work.shouldRun(context);
|
|
230
213
|
if (!shouldRun) {
|
|
231
214
|
const skippedResult = {
|
|
232
|
-
status: "skipped"
|
|
215
|
+
status: "skipped",
|
|
233
216
|
duration: Date.now() - workStartTime
|
|
234
217
|
};
|
|
235
218
|
context.workResults.set(work.name, skippedResult);
|
|
@@ -254,7 +237,7 @@ var Workflow = class {
|
|
|
254
237
|
const duration = Date.now() - result.startTime;
|
|
255
238
|
if ("error" in result && result.error) {
|
|
256
239
|
const workResult = {
|
|
257
|
-
status: "failed"
|
|
240
|
+
status: "failed",
|
|
258
241
|
error: result.error,
|
|
259
242
|
duration
|
|
260
243
|
};
|
|
@@ -265,7 +248,7 @@ var Workflow = class {
|
|
|
265
248
|
}
|
|
266
249
|
} else {
|
|
267
250
|
const workResult = {
|
|
268
|
-
status: "completed"
|
|
251
|
+
status: "completed",
|
|
269
252
|
result: result.result,
|
|
270
253
|
duration
|
|
271
254
|
};
|
|
@@ -285,9 +268,7 @@ var Workflow = class {
|
|
|
285
268
|
};
|
|
286
269
|
export {
|
|
287
270
|
Work,
|
|
288
|
-
WorkStatus,
|
|
289
271
|
Workflow,
|
|
290
|
-
WorkflowStatus,
|
|
291
272
|
getWorkDefinition
|
|
292
273
|
};
|
|
293
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 * The list of works in the workflow (readonly)\n */\n readonly works: readonly IWorkflowWork[];\n\n /**\n * The workflow options (readonly)\n */\n readonly options: Readonly<Required<WorkflowOptions>>;\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parallel(works: readonly IWorkDefinition<string, TData, any, TWorkResults>[]): IWorkflow<\n TData,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >;\n\n /**\n * Seal the workflow to prevent further modifications\n */\n seal(): ISealedWorkflow<TData, TWorkResults>;\n seal<TResult>(\n sealingWork: ISealingWorkDefinition<TData, TWorkResults, TResult>\n ): ISealedWorkflow<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 work definition for sealing a workflow.\n * Similar to IWorkDefinition but without 'name'.\n */\nexport type ISealingWorkDefinition<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n TResult = unknown,\n> = Omit<IWorkDefinition<'seal', TData, TResult, TWorkResults>, 'name'>;\n\n/**\n * A sealed workflow that can only be executed, not modified.\n * Use workflow.seal() to create a sealed workflow.\n * Picks `works`, `options`, `isSealed`, and `run` from IWorkflow, adds `name: 'seal'`.\n */\nexport type ISealedWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> = Pick<IWorkflow<TData, TWorkResults>, 'works' | 'options' | 'isSealed' | 'run'> & {\n readonly name: 'seal';\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 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 * The list of works in the workflow (readonly)\n */\n get works(): readonly IWorkflowWork[] {\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<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/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",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@eslint/js": "^9.39.2",
|
|
51
51
|
"@types/node": "^25.0.10",
|
|
52
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
52
53
|
"eslint": "^9.39.2",
|
|
53
54
|
"eslint-config-prettier": "^10.1.8",
|
|
54
55
|
"prettier": "^3.8.1",
|