@yigitahmetsahin/workflow-ts 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -184,6 +184,52 @@ const conditionalWork = new Work({
184
184
  });
185
185
  ```
186
186
 
187
+ ### `.seal()`
188
+
189
+ Seal the workflow to prevent further modifications. Returns an `ISealedWorkflow` that only exposes the `run()` method.
190
+
191
+ ```typescript
192
+ const sealed = new Workflow<{ userId: string }>()
193
+ .serial({
194
+ name: 'validate',
195
+ execute: async (ctx) => ctx.data.userId.length > 0,
196
+ })
197
+ .parallel([
198
+ { name: 'fetchOrders', execute: async (ctx) => [{ id: 1 }] },
199
+ { name: 'fetchProfile', execute: async (ctx) => ({ name: 'John' }) },
200
+ ])
201
+ .seal();
202
+
203
+ // TypeScript prevents further modifications:
204
+ // sealed.serial(...) // ❌ Error: Property 'serial' does not exist
205
+ // sealed.parallel(...) // ❌ Error: Property 'parallel' does not exist
206
+
207
+ // Only run() is available:
208
+ const result = await sealed.run({ userId: '123' }); // ✅ OK
209
+ ```
210
+
211
+ This is useful when you want to:
212
+
213
+ - **Enforce immutability** - Ensure the workflow definition cannot be accidentally modified after construction
214
+ - **Expose a clean API** - Pass a sealed workflow to other parts of your code that should only execute it, not modify it
215
+ - **Type safety** - Get compile-time errors if someone tries to add more works to a finalized workflow
216
+
217
+ ```typescript
218
+ // Example: Factory function that returns a sealed workflow
219
+ function createUserWorkflow(): ISealedWorkflow<{ userId: string }, { user: User }> {
220
+ return new Workflow<{ userId: string }>()
221
+ .serial({
222
+ name: 'user',
223
+ execute: async (ctx) => fetchUser(ctx.data.userId),
224
+ })
225
+ .seal();
226
+ }
227
+
228
+ // Consumers can only run the workflow
229
+ const workflow = createUserWorkflow();
230
+ const result = await workflow.run({ userId: '123' });
231
+ ```
232
+
187
233
  ### Error Silencing
188
234
 
189
235
  Use `silenceError: true` to allow a work to fail without stopping the workflow. The error is still recorded and accessible:
@@ -214,6 +260,75 @@ const result = await workflow.run({ userId: '123' });
214
260
  // result.status === WorkflowStatus.COMPLETED (workflow continues despite error)
215
261
  ```
216
262
 
263
+ ### Workflow Options
264
+
265
+ The `Workflow` constructor accepts an options object:
266
+
267
+ ```typescript
268
+ interface WorkflowOptions {
269
+ failFast?: boolean; // Stop on first error or continue (default: true)
270
+ }
271
+ ```
272
+
273
+ #### `failFast`
274
+
275
+ Controls whether the workflow stops immediately on the first error or continues executing remaining works:
276
+
277
+ ```typescript
278
+ // Default: failFast: true - stops on first error
279
+ const workflow = new Workflow<{ userId: string }>()
280
+ .serial({ name: 'work1', execute: async () => 'ok' })
281
+ .serial({
282
+ name: 'failing',
283
+ execute: async () => {
284
+ throw new Error('Stop here');
285
+ },
286
+ })
287
+ .serial({ name: 'work3', execute: async () => 'never runs' });
288
+ // work3 will NOT execute
289
+
290
+ // failFast: false - continues despite errors
291
+ const workflow = new Workflow<{ userId: string }>({ failFast: false })
292
+ .serial({ name: 'work1', execute: async () => 'ok' })
293
+ .serial({
294
+ name: 'failing',
295
+ execute: async () => {
296
+ throw new Error('Continue anyway');
297
+ },
298
+ })
299
+ .serial({ name: 'work3', execute: async () => 'still runs' });
300
+ // work3 WILL execute, but workflow still fails at the end
301
+
302
+ const result = await workflow.run({ userId: '123' });
303
+ // result.status === WorkflowStatus.FAILED
304
+ // result.error.message === 'Continue anyway' (first error)
305
+ // result.context.workResults.get('work3')?.result === 'still runs'
306
+ ```
307
+
308
+ You can combine `failFast: false` with work-level `silenceError` to run all works and complete successfully:
309
+
310
+ ```typescript
311
+ const workflow = new Workflow<{ userId: string }>({ failFast: false })
312
+ .serial({
313
+ name: 'optional1',
314
+ execute: async () => {
315
+ throw new Error('Ignored');
316
+ },
317
+ silenceError: true,
318
+ })
319
+ .serial({
320
+ name: 'optional2',
321
+ execute: async () => {
322
+ throw new Error('Also ignored');
323
+ },
324
+ silenceError: true,
325
+ })
326
+ .serial({ name: 'final', execute: async () => 'done' });
327
+
328
+ const result = await workflow.run({ userId: '123' });
329
+ // result.status === WorkflowStatus.COMPLETED (all errors silenced)
330
+ ```
331
+
217
332
  ### `.run(initialData)`
218
333
 
219
334
  Execute the workflow with initial data.
package/dist/index.cjs CHANGED
@@ -81,8 +81,9 @@ var WorkResultsMap = class {
81
81
  }
82
82
  };
83
83
  var Workflow = class {
84
- constructor() {
84
+ constructor(options = {}) {
85
85
  this.works = [];
86
+ this.options = { failFast: true, ...options };
86
87
  }
87
88
  /**
88
89
  * Add a serial work to the workflow.
@@ -116,6 +117,24 @@ var Workflow = class {
116
117
  });
117
118
  return this;
118
119
  }
120
+ /**
121
+ * Seal the workflow to prevent further modifications.
122
+ * Returns a SealedWorkflow that can only be executed with run().
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const sealed = new Workflow<{ userId: string }>()
127
+ * .serial({ name: 'step1', execute: async () => 'result' })
128
+ * .seal();
129
+ *
130
+ * // sealed.serial() - TypeScript error! Method doesn't exist
131
+ * // sealed.parallel() - TypeScript error! Method doesn't exist
132
+ * await sealed.run({ userId: '123' }); // OK
133
+ * ```
134
+ */
135
+ seal() {
136
+ return this;
137
+ }
119
138
  /**
120
139
  * Execute the workflow with initial data
121
140
  */
@@ -126,24 +145,42 @@ var Workflow = class {
126
145
  workResults: new WorkResultsMap()
127
146
  };
128
147
  const workResults = /* @__PURE__ */ new Map();
148
+ const collectedErrors = [];
129
149
  try {
130
150
  for (const workGroup of this.works) {
131
- if (workGroup.type === "serial") {
132
- await this.executeWork(
133
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
- workGroup.works[0],
135
- context,
136
- workResults
137
- );
138
- } else {
139
- await this.executeParallelWorks(
140
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
- workGroup.works,
142
- context,
143
- workResults
144
- );
151
+ try {
152
+ if (workGroup.type === "serial") {
153
+ await this.executeWork(
154
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
+ workGroup.works[0],
156
+ context,
157
+ workResults
158
+ );
159
+ } else {
160
+ await this.executeParallelWorks(
161
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
+ workGroup.works,
163
+ context,
164
+ workResults
165
+ );
166
+ }
167
+ } catch (error) {
168
+ const err = error instanceof Error ? error : new Error(String(error));
169
+ if (this.options.failFast) {
170
+ throw err;
171
+ }
172
+ collectedErrors.push(err);
145
173
  }
146
174
  }
175
+ if (collectedErrors.length > 0) {
176
+ return {
177
+ status: "failed" /* FAILED */,
178
+ context,
179
+ workResults,
180
+ totalDuration: Date.now() - startTime,
181
+ error: collectedErrors[0]
182
+ };
183
+ }
147
184
  return {
148
185
  status: "completed" /* COMPLETED */,
149
186
  context,
@@ -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","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} from './workflow.types';\nimport { WorkInput, getWorkDefinition } from './work';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, IWorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]> {\n const result = this.map.get(name);\n if (!result) {\n throw new Error(\n `Work result \"${String(name)}\" not found. This work may not have executed yet.`\n );\n }\n return result as IWorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has(name: string): boolean {\n return this.map.has(name as keyof TWorkResults);\n }\n}\n\n/**\n * A simple, extensible workflow engine that supports serial and parallel work execution.\n * Work names and result types are automatically inferred from the workflow definition.\n *\n * @example\n * ```typescript\n * const workflow = new Workflow<{ userId: string }>()\n * .serial({\n * name: 'validate',\n * execute: async (ctx) => true, // returns boolean\n * })\n * .parallel([\n * {\n * name: 'fetchOrders',\n * execute: async (ctx) => [{ id: 1 }], // returns Order[]\n * },\n * {\n * name: 'fetchProfile',\n * execute: async (ctx) => ({ name: 'John' }), // returns Profile\n * },\n * ])\n * .serial({\n * name: 'process',\n * execute: async (ctx) => {\n * // ✅ Autocomplete for names AND types are inferred!\n * const isValid = ctx.workResults.get('validate').result; // boolean | undefined\n * const orders = ctx.workResults.get('fetchOrders').result; // Order[] | undefined\n * const profile = ctx.workResults.get('fetchProfile').result; // Profile | undefined\n * return { orders, profile };\n * },\n * });\n *\n * const result = await workflow.run({ userId: '123' });\n * ```\n */\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n\n /**\n * Add a serial work to the workflow.\n * Accepts either an inline work definition or a Work instance.\n * The work name and result type are automatically inferred.\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\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 this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)) as unknown as IWorkDefinition<\n string,\n TData,\n unknown,\n TWorkResults\n >[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n\n try {\n for (const workGroup of this.works) {\n if (workGroup.type === 'serial') {\n await this.executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works[0] as IWorkDefinition<string, TData, any, any>,\n context,\n workResults\n );\n } else {\n await this.executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works as IWorkDefinition<string, TData, any, any>[],\n context,\n workResults\n );\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\n/**\n * Helper type to extract work results from parallel works array.\n * Since Work implements IWorkDefinition, we can use Extract directly.\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,eAAY;AACZ,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AALA,SAAAA;AAAA,GAAA;AAWL,IAAK,iBAAL,kBAAKC,oBAAL;AACL,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,eAAY;AACZ,EAAAA,gBAAA,YAAS;AAJC,SAAAA;AAAA,GAAA;;;ACML,IAAM,OAAN,MAKoE;AAAA,EAqBzE,YAAY,YAA2E;AACrF,SAAK,OAAO,WAAW;AACvB,SAAK,UAAU,WAAW;AAC1B,SAAK,YAAY,WAAW;AAC5B,SAAK,UAAU,WAAW;AAC1B,SAAK,eAAe,WAAW;AAAA,EACjC;AACF;AAiBO,SAAS,kBAMd,OAC+D;AAC/D,SAAO;AACT;;;AChEA,IAAM,iBAAN,MAE2C;AAAA,EAF3C;AAGE,SAAQ,MAAM,oBAAI,IAA8C;AAAA;AAAA,EAEhE,IAAkC,MAAuC;AACvE,UAAM,SAAS,KAAK,IAAI,IAAI,IAAI;AAChC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,gBAAgB,OAAO,IAAI,CAAC;AAAA,MAC9B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAkC,MAAS,OAA2C;AACpF,SAAK,IAAI,IAAI,MAAM,KAAK;AAAA,EAC1B;AAAA,EAEA,IAAI,MAAuB;AACzB,WAAO,KAAK,IAAI,IAAI,IAA0B;AAAA,EAChD;AACF;AAqCO,IAAM,WAAN,MAGL;AAAA,EAHK;AAIL,SAAQ,QAAyB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,OACE,MAC2D;AAC3D,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,CAAC,kBAAkB,IAAI,CAAC;AAAA,IACjC,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,SACE,OACuE;AACvE,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAM9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,aAAmE;AAC3E,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,UAAiD;AAAA,MACrD,MAAM;AAAA,MACN,aAAa,IAAI,eAA6B;AAAA,IAChD;AACA,UAAM,cAAc,oBAAI,IAAqC;AAE7D,QAAI;AACF,iBAAW,aAAa,KAAK,OAAO;AAClC,YAAI,UAAU,SAAS,UAAU;AAC/B,gBAAM,KAAK;AAAA;AAAA,YAET,UAAU,MAAM,CAAC;AAAA,YACjB;AAAA,YACA;AAAA,UACF;AAAA,QACF,OAAO;AACL,gBAAM,KAAK;AAAA;AAAA,YAET,UAAU;AAAA,YACV;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,QAC5B,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAEZ,MACA,SACA,aACe;AACf,UAAM,gBAAgB,KAAK,IAAI;AAG/B,QAAI,KAAK,WAAW;AAClB,YAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,UAAI,CAAC,WAAW;AACd,cAAM,gBAA6B;AAAA,UACjC;AAAA,UACA,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAEA,gBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,oBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AAEzC,YAAM,aAA0B;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAIA,cAAQ,YAAY,IAAI,KAAK,MAA4B,UAAiB;AAC1E,kBAAY,IAAI,KAAK,MAA4B,UAAU;AAAA,IAC7D,SAAS,OAAO;AACd,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAEpE,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,QACP,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAGA,cAAQ,YAAY,IAAI,KAAK,MAA4B,YAAmB;AAC5E,kBAAY,IAAI,KAAK,MAA4B,YAAY;AAG7D,UAAI,KAAK,SAAS;AAChB,cAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,MACjC;AAGA,UAAI,CAAC,KAAK,cAAc;AACtB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAEZ,OACA,SACA,aACe;AACf,UAAM,WAAW,MAAM,IAAI,OAAO,SAAS;AACzC,YAAM,gBAAgB,KAAK,IAAI;AAG/B,UAAI,KAAK,WAAW;AAClB,cAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,YAAI,CAAC,WAAW;AACd,gBAAM,gBAA6B;AAAA,YACjC;AAAA,YACA,UAAU,KAAK,IAAI,IAAI;AAAA,UACzB;AAEA,kBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,sBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D,iBAAO,EAAE,MAAM,SAAS,KAAK;AAAA,QAC/B;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AACzC,eAAO,EAAE,MAAM,QAAQ,WAAW,cAAc;AAAA,MAClD,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,eAAO,EAAE,MAAM,OAAO,KAAK,WAAW,cAAc;AAAA,MACtD;AAAA,IACF,CAAC;AAED,UAAM,UAAU,MAAM,QAAQ,IAAI,QAAQ;AAI1C,UAAM,SAA6E,CAAC;AAEpF,eAAW,UAAU,SAAS;AAC5B,UAAI,aAAa,UAAU,OAAO,SAAS;AACzC;AAAA,MACF;AAEA,YAAM,WAAW,KAAK,IAAI,IAAI,OAAO;AAErC,UAAI,WAAW,UAAU,OAAO,OAAO;AACrC,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,OAAO,OAAO;AAAA,UACd;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAElE,YAAI,CAAC,OAAO,KAAK,cAAc;AAC7B,iBAAO,KAAK,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,QACxD;AAAA,MACF,OAAO;AACL,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,QAAQ,OAAO;AAAA,UACf;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAAA,MACpE;AAAA,IACF;AAGA,eAAW,UAAU,SAAS;AAC5B,UAAI,WAAW,UAAU,OAAO,SAAS,OAAO,KAAK,SAAS;AAC5D,cAAM,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO;AAAA,MACjD;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,OAAO,CAAC,EAAE;AAAA,IAClB;AAAA,EACF;AACF;","names":["WorkStatus","WorkflowStatus"]}
1
+ {"version":3,"sources":["../src/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 * A sealed workflow that can only be executed, not modified.\n * Use workflow.seal() to create a sealed workflow.\n */\nexport interface ISealedWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Execute the workflow with initial data\n */\n run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;\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 ISealedWorkflow,\n} from './workflow.types';\nimport { WorkInput, getWorkDefinition } from './work';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, IWorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]> {\n const result = this.map.get(name);\n if (!result) {\n throw new Error(\n `Work result \"${String(name)}\" not found. This work may not have executed yet.`\n );\n }\n return result as IWorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has(name: string): boolean {\n return this.map.has(name as keyof TWorkResults);\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 */\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\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n private options: Required<WorkflowOptions>;\n\n constructor(options: WorkflowOptions = {}) {\n this.options = { failFast: true, ...options };\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 */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\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 this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)) as unknown as IWorkDefinition<\n string,\n TData,\n unknown,\n TWorkResults\n >[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * 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.serial() - TypeScript error! Method doesn't exist\n * // sealed.parallel() - TypeScript error! Method doesn't exist\n * await sealed.run({ userId: '123' }); // OK\n * ```\n */\n seal(): ISealedWorkflow<TData, TWorkResults> {\n return this;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n const collectedErrors: Error[] = [];\n\n try {\n for (const workGroup of this.works) {\n try {\n if (workGroup.type === 'serial') {\n await this.executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works[0] as IWorkDefinition<string, TData, any, any>,\n context,\n workResults\n );\n } else {\n await this.executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works as IWorkDefinition<string, TData, any, any>[],\n context,\n workResults\n );\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\n/**\n * Helper type to extract work results from parallel works array.\n * Since Work implements IWorkDefinition, we can use Extract directly.\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":"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","names":["WorkStatus","WorkflowStatus"]}
package/dist/index.d.cts CHANGED
@@ -79,6 +79,16 @@ interface IWorkflowResult<TData = Record<string, unknown>, TWorkResults extends
79
79
  totalDuration: number;
80
80
  error?: Error;
81
81
  }
82
+ /**
83
+ * A sealed workflow that can only be executed, not modified.
84
+ * Use workflow.seal() to create a sealed workflow.
85
+ */
86
+ interface ISealedWorkflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
87
+ /**
88
+ * Execute the workflow with initial data
89
+ */
90
+ run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;
91
+ }
82
92
 
83
93
  /**
84
94
  * A standalone Work unit that can be added to workflows.
@@ -157,8 +167,22 @@ declare function getWorkDefinition<TName extends string, TData, TResult, TAvaila
157
167
  * const result = await workflow.run({ userId: '123' });
158
168
  * ```
159
169
  */
170
+ /**
171
+ * Options for configuring workflow behavior
172
+ */
173
+ interface WorkflowOptions {
174
+ /**
175
+ * Whether to stop execution immediately when a work fails.
176
+ * - true: Stop on first failure (default)
177
+ * - false: Continue executing remaining works, fail at the end if any work failed
178
+ * @default true
179
+ */
180
+ failFast?: boolean;
181
+ }
160
182
  declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = NonNullable<unknown>> {
161
183
  private works;
184
+ private options;
185
+ constructor(options?: WorkflowOptions);
162
186
  /**
163
187
  * Add a serial work to the workflow.
164
188
  * Accepts either an inline work definition or a Work instance.
@@ -181,6 +205,22 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
181
205
  * ```
182
206
  */
183
207
  parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(works: TParallelWorks): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;
208
+ /**
209
+ * Seal the workflow to prevent further modifications.
210
+ * Returns a SealedWorkflow that can only be executed with run().
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const sealed = new Workflow<{ userId: string }>()
215
+ * .serial({ name: 'step1', execute: async () => 'result' })
216
+ * .seal();
217
+ *
218
+ * // sealed.serial() - TypeScript error! Method doesn't exist
219
+ * // sealed.parallel() - TypeScript error! Method doesn't exist
220
+ * await sealed.run({ userId: '123' }); // OK
221
+ * ```
222
+ */
223
+ seal(): ISealedWorkflow<TData, TWorkResults>;
184
224
  /**
185
225
  * Execute the workflow with initial data
186
226
  */
@@ -204,4 +244,4 @@ type ParallelWorksToRecord<T extends readonly IWorkDefinition<string, any, any,
204
244
  }> extends IWorkDefinition<string, any, infer R, any> ? R : never;
205
245
  };
206
246
 
207
- export { type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, Work, type WorkInput, WorkStatus, Workflow, WorkflowStatus, getWorkDefinition };
247
+ export { type ISealedWorkflow, type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, Work, type WorkInput, WorkStatus, Workflow, type WorkflowOptions, WorkflowStatus, getWorkDefinition };
package/dist/index.d.ts CHANGED
@@ -79,6 +79,16 @@ interface IWorkflowResult<TData = Record<string, unknown>, TWorkResults extends
79
79
  totalDuration: number;
80
80
  error?: Error;
81
81
  }
82
+ /**
83
+ * A sealed workflow that can only be executed, not modified.
84
+ * Use workflow.seal() to create a sealed workflow.
85
+ */
86
+ interface ISealedWorkflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = Record<string, unknown>> {
87
+ /**
88
+ * Execute the workflow with initial data
89
+ */
90
+ run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;
91
+ }
82
92
 
83
93
  /**
84
94
  * A standalone Work unit that can be added to workflows.
@@ -157,8 +167,22 @@ declare function getWorkDefinition<TName extends string, TData, TResult, TAvaila
157
167
  * const result = await workflow.run({ userId: '123' });
158
168
  * ```
159
169
  */
170
+ /**
171
+ * Options for configuring workflow behavior
172
+ */
173
+ interface WorkflowOptions {
174
+ /**
175
+ * Whether to stop execution immediately when a work fails.
176
+ * - true: Stop on first failure (default)
177
+ * - false: Continue executing remaining works, fail at the end if any work failed
178
+ * @default true
179
+ */
180
+ failFast?: boolean;
181
+ }
160
182
  declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Record<string, unknown> = NonNullable<unknown>> {
161
183
  private works;
184
+ private options;
185
+ constructor(options?: WorkflowOptions);
162
186
  /**
163
187
  * Add a serial work to the workflow.
164
188
  * Accepts either an inline work definition or a Work instance.
@@ -181,6 +205,22 @@ declare class Workflow<TData = Record<string, unknown>, TWorkResults extends Rec
181
205
  * ```
182
206
  */
183
207
  parallel<const TParallelWorks extends readonly WorkInput<string, TData, unknown, TWorkResults>[]>(works: TParallelWorks): Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;
208
+ /**
209
+ * Seal the workflow to prevent further modifications.
210
+ * Returns a SealedWorkflow that can only be executed with run().
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const sealed = new Workflow<{ userId: string }>()
215
+ * .serial({ name: 'step1', execute: async () => 'result' })
216
+ * .seal();
217
+ *
218
+ * // sealed.serial() - TypeScript error! Method doesn't exist
219
+ * // sealed.parallel() - TypeScript error! Method doesn't exist
220
+ * await sealed.run({ userId: '123' }); // OK
221
+ * ```
222
+ */
223
+ seal(): ISealedWorkflow<TData, TWorkResults>;
184
224
  /**
185
225
  * Execute the workflow with initial data
186
226
  */
@@ -204,4 +244,4 @@ type ParallelWorksToRecord<T extends readonly IWorkDefinition<string, any, any,
204
244
  }> extends IWorkDefinition<string, any, infer R, any> ? R : never;
205
245
  };
206
246
 
207
- export { type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, Work, type WorkInput, WorkStatus, Workflow, WorkflowStatus, getWorkDefinition };
247
+ export { type ISealedWorkflow, type IWorkDefinition, type IWorkResult, type IWorkResultsMap, type IWorkflowContext, type IWorkflowResult, type IWorkflowWork, Work, type WorkInput, WorkStatus, Workflow, type WorkflowOptions, WorkflowStatus, getWorkDefinition };
package/dist/index.js CHANGED
@@ -51,8 +51,9 @@ var WorkResultsMap = class {
51
51
  }
52
52
  };
53
53
  var Workflow = class {
54
- constructor() {
54
+ constructor(options = {}) {
55
55
  this.works = [];
56
+ this.options = { failFast: true, ...options };
56
57
  }
57
58
  /**
58
59
  * Add a serial work to the workflow.
@@ -86,6 +87,24 @@ var Workflow = class {
86
87
  });
87
88
  return this;
88
89
  }
90
+ /**
91
+ * Seal the workflow to prevent further modifications.
92
+ * Returns a SealedWorkflow that can only be executed with run().
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const sealed = new Workflow<{ userId: string }>()
97
+ * .serial({ name: 'step1', execute: async () => 'result' })
98
+ * .seal();
99
+ *
100
+ * // sealed.serial() - TypeScript error! Method doesn't exist
101
+ * // sealed.parallel() - TypeScript error! Method doesn't exist
102
+ * await sealed.run({ userId: '123' }); // OK
103
+ * ```
104
+ */
105
+ seal() {
106
+ return this;
107
+ }
89
108
  /**
90
109
  * Execute the workflow with initial data
91
110
  */
@@ -96,24 +115,42 @@ var Workflow = class {
96
115
  workResults: new WorkResultsMap()
97
116
  };
98
117
  const workResults = /* @__PURE__ */ new Map();
118
+ const collectedErrors = [];
99
119
  try {
100
120
  for (const workGroup of this.works) {
101
- if (workGroup.type === "serial") {
102
- await this.executeWork(
103
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
- workGroup.works[0],
105
- context,
106
- workResults
107
- );
108
- } else {
109
- await this.executeParallelWorks(
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
- workGroup.works,
112
- context,
113
- workResults
114
- );
121
+ try {
122
+ if (workGroup.type === "serial") {
123
+ await this.executeWork(
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ workGroup.works[0],
126
+ context,
127
+ workResults
128
+ );
129
+ } else {
130
+ await this.executeParallelWorks(
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ workGroup.works,
133
+ context,
134
+ workResults
135
+ );
136
+ }
137
+ } catch (error) {
138
+ const err = error instanceof Error ? error : new Error(String(error));
139
+ if (this.options.failFast) {
140
+ throw err;
141
+ }
142
+ collectedErrors.push(err);
115
143
  }
116
144
  }
145
+ if (collectedErrors.length > 0) {
146
+ return {
147
+ status: "failed" /* FAILED */,
148
+ context,
149
+ workResults,
150
+ totalDuration: Date.now() - startTime,
151
+ error: collectedErrors[0]
152
+ };
153
+ }
117
154
  return {
118
155
  status: "completed" /* COMPLETED */,
119
156
  context,
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","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} from './workflow.types';\nimport { WorkInput, getWorkDefinition } from './work';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, IWorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]> {\n const result = this.map.get(name);\n if (!result) {\n throw new Error(\n `Work result \"${String(name)}\" not found. This work may not have executed yet.`\n );\n }\n return result as IWorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has(name: string): boolean {\n return this.map.has(name as keyof TWorkResults);\n }\n}\n\n/**\n * A simple, extensible workflow engine that supports serial and parallel work execution.\n * Work names and result types are automatically inferred from the workflow definition.\n *\n * @example\n * ```typescript\n * const workflow = new Workflow<{ userId: string }>()\n * .serial({\n * name: 'validate',\n * execute: async (ctx) => true, // returns boolean\n * })\n * .parallel([\n * {\n * name: 'fetchOrders',\n * execute: async (ctx) => [{ id: 1 }], // returns Order[]\n * },\n * {\n * name: 'fetchProfile',\n * execute: async (ctx) => ({ name: 'John' }), // returns Profile\n * },\n * ])\n * .serial({\n * name: 'process',\n * execute: async (ctx) => {\n * // ✅ Autocomplete for names AND types are inferred!\n * const isValid = ctx.workResults.get('validate').result; // boolean | undefined\n * const orders = ctx.workResults.get('fetchOrders').result; // Order[] | undefined\n * const profile = ctx.workResults.get('fetchProfile').result; // Profile | undefined\n * return { orders, profile };\n * },\n * });\n *\n * const result = await workflow.run({ userId: '123' });\n * ```\n */\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n\n /**\n * Add a serial work to the workflow.\n * Accepts either an inline work definition or a Work instance.\n * The work name and result type are automatically inferred.\n */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\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 this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)) as unknown as IWorkDefinition<\n string,\n TData,\n unknown,\n TWorkResults\n >[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n\n try {\n for (const workGroup of this.works) {\n if (workGroup.type === 'serial') {\n await this.executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works[0] as IWorkDefinition<string, TData, any, any>,\n context,\n workResults\n );\n } else {\n await this.executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works as IWorkDefinition<string, TData, any, any>[],\n context,\n workResults\n );\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\n/**\n * Helper type to extract work results from parallel works array.\n * Since Work implements IWorkDefinition, we can use Extract directly.\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":";AAGO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,eAAY;AACZ,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AALA,SAAAA;AAAA,GAAA;AAWL,IAAK,iBAAL,kBAAKC,oBAAL;AACL,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,eAAY;AACZ,EAAAA,gBAAA,YAAS;AAJC,SAAAA;AAAA,GAAA;;;ACML,IAAM,OAAN,MAKoE;AAAA,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;;;AChEA,IAAM,iBAAN,MAE2C;AAAA,EAF3C;AAGE,SAAQ,MAAM,oBAAI,IAA8C;AAAA;AAAA,EAEhE,IAAkC,MAAuC;AACvE,UAAM,SAAS,KAAK,IAAI,IAAI,IAAI;AAChC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,gBAAgB,OAAO,IAAI,CAAC;AAAA,MAC9B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAkC,MAAS,OAA2C;AACpF,SAAK,IAAI,IAAI,MAAM,KAAK;AAAA,EAC1B;AAAA,EAEA,IAAI,MAAuB;AACzB,WAAO,KAAK,IAAI,IAAI,IAA0B;AAAA,EAChD;AACF;AAqCO,IAAM,WAAN,MAGL;AAAA,EAHK;AAIL,SAAQ,QAAyB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,OACE,MAC2D;AAC3D,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,CAAC,kBAAkB,IAAI,CAAC;AAAA,IACjC,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,SACE,OACuE;AACvE,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAM9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,aAAmE;AAC3E,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,UAAiD;AAAA,MACrD,MAAM;AAAA,MACN,aAAa,IAAI,eAA6B;AAAA,IAChD;AACA,UAAM,cAAc,oBAAI,IAAqC;AAE7D,QAAI;AACF,iBAAW,aAAa,KAAK,OAAO;AAClC,YAAI,UAAU,SAAS,UAAU;AAC/B,gBAAM,KAAK;AAAA;AAAA,YAET,UAAU,MAAM,CAAC;AAAA,YACjB;AAAA,YACA;AAAA,UACF;AAAA,QACF,OAAO;AACL,gBAAM,KAAK;AAAA;AAAA,YAET,UAAU;AAAA,YACV;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,QAC5B,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAEZ,MACA,SACA,aACe;AACf,UAAM,gBAAgB,KAAK,IAAI;AAG/B,QAAI,KAAK,WAAW;AAClB,YAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,UAAI,CAAC,WAAW;AACd,cAAM,gBAA6B;AAAA,UACjC;AAAA,UACA,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAEA,gBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,oBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AAEzC,YAAM,aAA0B;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAIA,cAAQ,YAAY,IAAI,KAAK,MAA4B,UAAiB;AAC1E,kBAAY,IAAI,KAAK,MAA4B,UAAU;AAAA,IAC7D,SAAS,OAAO;AACd,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAEpE,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,QACP,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAGA,cAAQ,YAAY,IAAI,KAAK,MAA4B,YAAmB;AAC5E,kBAAY,IAAI,KAAK,MAA4B,YAAY;AAG7D,UAAI,KAAK,SAAS;AAChB,cAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,MACjC;AAGA,UAAI,CAAC,KAAK,cAAc;AACtB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAEZ,OACA,SACA,aACe;AACf,UAAM,WAAW,MAAM,IAAI,OAAO,SAAS;AACzC,YAAM,gBAAgB,KAAK,IAAI;AAG/B,UAAI,KAAK,WAAW;AAClB,cAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,YAAI,CAAC,WAAW;AACd,gBAAM,gBAA6B;AAAA,YACjC;AAAA,YACA,UAAU,KAAK,IAAI,IAAI;AAAA,UACzB;AAEA,kBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,sBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D,iBAAO,EAAE,MAAM,SAAS,KAAK;AAAA,QAC/B;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AACzC,eAAO,EAAE,MAAM,QAAQ,WAAW,cAAc;AAAA,MAClD,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,eAAO,EAAE,MAAM,OAAO,KAAK,WAAW,cAAc;AAAA,MACtD;AAAA,IACF,CAAC;AAED,UAAM,UAAU,MAAM,QAAQ,IAAI,QAAQ;AAI1C,UAAM,SAA6E,CAAC;AAEpF,eAAW,UAAU,SAAS;AAC5B,UAAI,aAAa,UAAU,OAAO,SAAS;AACzC;AAAA,MACF;AAEA,YAAM,WAAW,KAAK,IAAI,IAAI,OAAO;AAErC,UAAI,WAAW,UAAU,OAAO,OAAO;AACrC,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,OAAO,OAAO;AAAA,UACd;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAElE,YAAI,CAAC,OAAO,KAAK,cAAc;AAC7B,iBAAO,KAAK,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,QACxD;AAAA,MACF,OAAO;AACL,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,QAAQ,OAAO;AAAA,UACf;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAAA,MACpE;AAAA,IACF;AAGA,eAAW,UAAU,SAAS;AAC5B,UAAI,WAAW,UAAU,OAAO,SAAS,OAAO,KAAK,SAAS;AAC5D,cAAM,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO;AAAA,MACjD;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,OAAO,CAAC,EAAE;AAAA,IAClB;AAAA,EACF;AACF;","names":["WorkStatus","WorkflowStatus"]}
1
+ {"version":3,"sources":["../src/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 * A sealed workflow that can only be executed, not modified.\n * Use workflow.seal() to create a sealed workflow.\n */\nexport interface ISealedWorkflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Execute the workflow with initial data\n */\n run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>>;\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 ISealedWorkflow,\n} from './workflow.types';\nimport { WorkInput, getWorkDefinition } from './work';\n\n/**\n * Internal implementation of IWorkResultsMap using a Map\n */\nclass WorkResultsMap<\n TWorkResults extends Record<string, unknown>,\n> implements IWorkResultsMap<TWorkResults> {\n private map = new Map<keyof TWorkResults, IWorkResult<unknown>>();\n\n get<K extends keyof TWorkResults>(name: K): IWorkResult<TWorkResults[K]> {\n const result = this.map.get(name);\n if (!result) {\n throw new Error(\n `Work result \"${String(name)}\" not found. This work may not have executed yet.`\n );\n }\n return result as IWorkResult<TWorkResults[K]>;\n }\n\n set<K extends keyof TWorkResults>(name: K, value: IWorkResult<TWorkResults[K]>): void {\n this.map.set(name, value);\n }\n\n has(name: string): boolean {\n return this.map.has(name as keyof TWorkResults);\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 */\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\nexport class Workflow<\n TData = Record<string, unknown>,\n TWorkResults extends Record<string, unknown> = NonNullable<unknown>,\n> {\n private works: IWorkflowWork[] = [];\n private options: Required<WorkflowOptions>;\n\n constructor(options: WorkflowOptions = {}) {\n this.options = { failFast: true, ...options };\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 */\n serial<TName extends string, TResult>(\n work: WorkInput<TName, TData, TResult, TWorkResults>\n ): Workflow<TData, TWorkResults & { [K in TName]: TResult }> {\n this.works.push({\n type: 'serial',\n works: [getWorkDefinition(work)],\n });\n return this as unknown as Workflow<TData, TWorkResults & { [K in TName]: TResult }>;\n }\n\n /**\n * Add parallel works to the workflow.\n * Accepts an array of work definitions or Work instances.\n * All work names and result types are automatically inferred.\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 this.works.push({\n type: 'parallel',\n works: works.map((w) => getWorkDefinition(w)) as unknown as IWorkDefinition<\n string,\n TData,\n unknown,\n TWorkResults\n >[],\n });\n return this as unknown as Workflow<TData, TWorkResults & ParallelWorksToRecord<TParallelWorks>>;\n }\n\n /**\n * 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.serial() - TypeScript error! Method doesn't exist\n * // sealed.parallel() - TypeScript error! Method doesn't exist\n * await sealed.run({ userId: '123' }); // OK\n * ```\n */\n seal(): ISealedWorkflow<TData, TWorkResults> {\n return this;\n }\n\n /**\n * Execute the workflow with initial data\n */\n async run(initialData: TData): Promise<IWorkflowResult<TData, TWorkResults>> {\n const startTime = Date.now();\n const context: IWorkflowContext<TData, TWorkResults> = {\n data: initialData,\n workResults: new WorkResultsMap<TWorkResults>(),\n };\n const workResults = new Map<keyof TWorkResults, IWorkResult>();\n const collectedErrors: Error[] = [];\n\n try {\n for (const workGroup of this.works) {\n try {\n if (workGroup.type === 'serial') {\n await this.executeWork(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works[0] as IWorkDefinition<string, TData, any, any>,\n context,\n workResults\n );\n } else {\n await this.executeParallelWorks(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n workGroup.works as IWorkDefinition<string, TData, any, any>[],\n context,\n workResults\n );\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\n/**\n * Helper type to extract work results from parallel works array.\n * Since Work implements IWorkDefinition, we can use Extract directly.\n */\ntype ParallelWorksToRecord<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends readonly IWorkDefinition<string, any, any, any>[],\n> = {\n [K in T[number]['name']]: Extract<\n T[number],\n { name: K }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n > extends IWorkDefinition<string, any, infer R, any>\n ? R\n : never;\n};\n"],"mappings":";AAGO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,eAAY;AACZ,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AALA,SAAAA;AAAA,GAAA;AAWL,IAAK,iBAAL,kBAAKC,oBAAL;AACL,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,aAAU;AACV,EAAAA,gBAAA,eAAY;AACZ,EAAAA,gBAAA,YAAS;AAJC,SAAAA;AAAA,GAAA;;;ACML,IAAM,OAAN,MAKoE;AAAA,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;;;AC/DA,IAAM,iBAAN,MAE2C;AAAA,EAF3C;AAGE,SAAQ,MAAM,oBAAI,IAA8C;AAAA;AAAA,EAEhE,IAAkC,MAAuC;AACvE,UAAM,SAAS,KAAK,IAAI,IAAI,IAAI;AAChC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,gBAAgB,OAAO,IAAI,CAAC;AAAA,MAC9B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAkC,MAAS,OAA2C;AACpF,SAAK,IAAI,IAAI,MAAM,KAAK;AAAA,EAC1B;AAAA,EAEA,IAAI,MAAuB;AACzB,WAAO,KAAK,IAAI,IAAI,IAA0B;AAAA,EAChD;AACF;AAkDO,IAAM,WAAN,MAGL;AAAA,EAIA,YAAY,UAA2B,CAAC,GAAG;AAH3C,SAAQ,QAAyB,CAAC;AAIhC,SAAK,UAAU,EAAE,UAAU,MAAM,GAAG,QAAQ;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OACE,MAC2D;AAC3D,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,CAAC,kBAAkB,IAAI,CAAC;AAAA,IACjC,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,SACE,OACuE;AACvE,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAM9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,OAA6C;AAC3C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,aAAmE;AAC3E,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,UAAiD;AAAA,MACrD,MAAM;AAAA,MACN,aAAa,IAAI,eAA6B;AAAA,IAChD;AACA,UAAM,cAAc,oBAAI,IAAqC;AAC7D,UAAM,kBAA2B,CAAC;AAElC,QAAI;AACF,iBAAW,aAAa,KAAK,OAAO;AAClC,YAAI;AACF,cAAI,UAAU,SAAS,UAAU;AAC/B,kBAAM,KAAK;AAAA;AAAA,cAET,UAAU,MAAM,CAAC;AAAA,cACjB;AAAA,cACA;AAAA,YACF;AAAA,UACF,OAAO;AACL,kBAAM,KAAK;AAAA;AAAA,cAET,UAAU;AAAA,cACV;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,cAAI,KAAK,QAAQ,UAAU;AACzB,kBAAM;AAAA,UACR;AACA,0BAAgB,KAAK,GAAG;AAAA,QAC1B;AAAA,MACF;AAGA,UAAI,gBAAgB,SAAS,GAAG;AAC9B,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA,eAAe,KAAK,IAAI,IAAI;AAAA,UAC5B,OAAO,gBAAgB,CAAC;AAAA,QAC1B;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,MAC9B;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,KAAK,IAAI,IAAI;AAAA,QAC5B,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAEZ,MACA,SACA,aACe;AACf,UAAM,gBAAgB,KAAK,IAAI;AAG/B,QAAI,KAAK,WAAW;AAClB,YAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,UAAI,CAAC,WAAW;AACd,cAAM,gBAA6B;AAAA,UACjC;AAAA,UACA,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAEA,gBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,oBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AAEzC,YAAM,aAA0B;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAIA,cAAQ,YAAY,IAAI,KAAK,MAA4B,UAAiB;AAC1E,kBAAY,IAAI,KAAK,MAA4B,UAAU;AAAA,IAC7D,SAAS,OAAO;AACd,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAEpE,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,QACP,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB;AAGA,cAAQ,YAAY,IAAI,KAAK,MAA4B,YAAmB;AAC5E,kBAAY,IAAI,KAAK,MAA4B,YAAY;AAG7D,UAAI,KAAK,SAAS;AAChB,cAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,MACjC;AAGA,UAAI,CAAC,KAAK,cAAc;AACtB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAEZ,OACA,SACA,aACe;AACf,UAAM,WAAW,MAAM,IAAI,OAAO,SAAS;AACzC,YAAM,gBAAgB,KAAK,IAAI;AAG/B,UAAI,KAAK,WAAW;AAClB,cAAM,YAAY,MAAM,KAAK,UAAU,OAAO;AAC9C,YAAI,CAAC,WAAW;AACd,gBAAM,gBAA6B;AAAA,YACjC;AAAA,YACA,UAAU,KAAK,IAAI,IAAI;AAAA,UACzB;AAEA,kBAAQ,YAAY,IAAI,KAAK,MAA4B,aAAoB;AAC7E,sBAAY,IAAI,KAAK,MAA4B,aAAa;AAC9D,iBAAO,EAAE,MAAM,SAAS,KAAK;AAAA,QAC/B;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,QAAQ,OAAO;AACzC,eAAO,EAAE,MAAM,QAAQ,WAAW,cAAc;AAAA,MAClD,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,eAAO,EAAE,MAAM,OAAO,KAAK,WAAW,cAAc;AAAA,MACtD;AAAA,IACF,CAAC;AAED,UAAM,UAAU,MAAM,QAAQ,IAAI,QAAQ;AAI1C,UAAM,SAA6E,CAAC;AAEpF,eAAW,UAAU,SAAS;AAC5B,UAAI,aAAa,UAAU,OAAO,SAAS;AACzC;AAAA,MACF;AAEA,YAAM,WAAW,KAAK,IAAI,IAAI,OAAO;AAErC,UAAI,WAAW,UAAU,OAAO,OAAO;AACrC,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,OAAO,OAAO;AAAA,UACd;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAElE,YAAI,CAAC,OAAO,KAAK,cAAc;AAC7B,iBAAO,KAAK,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC;AAAA,QACxD;AAAA,MACF,OAAO;AACL,cAAM,aAA0B;AAAA,UAC9B;AAAA,UACA,QAAQ,OAAO;AAAA,UACf;AAAA,QACF;AAEA,gBAAQ,YAAY,IAAI,OAAO,KAAK,MAA4B,UAAiB;AACjF,oBAAY,IAAI,OAAO,KAAK,MAA4B,UAAU;AAAA,MACpE;AAAA,IACF;AAGA,eAAW,UAAU,SAAS;AAC5B,UAAI,WAAW,UAAU,OAAO,SAAS,OAAO,KAAK,SAAS;AAC5D,cAAM,OAAO,KAAK,QAAQ,OAAO,OAAO,OAAO;AAAA,MACjD;AAAA,IACF;AAGA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,OAAO,CAAC,EAAE;AAAA,IAClB;AAAA,EACF;AACF;","names":["WorkStatus","WorkflowStatus"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yigitahmetsahin/workflow-ts",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "A simple, extensible TypeScript workflow engine supporting serial and parallel work execution with full type inference",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",