{"_id":"@amwpcn/step","_rev":"4-93198cce3d3b10f5f9d3efa5b2d2302f","name":"@amwpcn/step","dist-tags":{"latest":"0.0.5"},"versions":{"0.0.1":{"name":"@amwpcn/step","version":"0.0.1","author":{"name":"Achintha Weerasinghe","email":"achinthamadumal@gmail.com"},"license":"MIT","_id":"@amwpcn/step@0.0.1","maintainers":[{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"}],"homepage":"https://github.com/amwpcn/amwpcn","bugs":{"url":"https://github.com/amwpcn/amwpcn/issues"},"dist":{"shasum":"5d2aee3b8505e8e651c490b598b793b7096a31d8","tarball":"https://registry.npmjs.org/@amwpcn/step/-/step-0.0.1.tgz","fileCount":35,"integrity":"sha512-lEvRl7E3speILUd4PVhzMn3ebIlmj3/si8CBWXvhhFCkaKhi/p134c8yTFwOwRqD4xffhrmXzGaBxBzNTUpE2w==","signatures":[{"sig":"MEUCIES+OItIihK8Zj027kXh8zDOKeArD0pM8JeLPr7dWNEsAiEA5SCmXCcZ2fsKBf6VqZtzcAX4H2U7uQgU8xlsA+H7WYA=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":59154},"main":"dist/index","types":"./dist/index.d.ts","engines":{"node":">=16"},"gitHead":"e85334b5e4c559dd822b4d5ceb99e62dbfe75080","scripts":{"test":"jest","build":"yarn clean && tsc","clean":"rimraf dist","start":"yarn build"},"_npmUser":{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"},"repository":{"url":"git+https://github.com/amwpcn/amwpcn.git","type":"git"},"_npmVersion":"10.7.0","description":"### TODO:","directories":{},"_nodeVersion":"20.15.1","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","rimraf":"^6.0.1","ts-jest":"^29.2.3","typescript":"^5.5.3","@types/jest":"^29.5.12","@types/node":"^20.14.11"},"_npmOperationalInternal":{"tmp":"tmp/step_0.0.1_1721926296599_0.14483767198969444","host":"s3://npm-registry-packages"}},"0.0.2":{"name":"@amwpcn/step","version":"0.0.2","author":{"name":"Achintha Weerasinghe","email":"achinthamadumal@gmail.com"},"license":"MIT","_id":"@amwpcn/step@0.0.2","maintainers":[{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"}],"homepage":"https://github.com/amwpcn/amwpcn","bugs":{"url":"https://github.com/amwpcn/amwpcn/issues"},"dist":{"shasum":"27b82bf874dfccc776faa58e3774b468642d61f1","tarball":"https://registry.npmjs.org/@amwpcn/step/-/step-0.0.2.tgz","fileCount":35,"integrity":"sha512-YjyxL/3Ds/p+M1CcLHojAAkl6UW+2hL+OqpGVtd8cJRUF/AnwWrdGWedbq34bp0Cp0PYBueP2ZBC0+MKspqj9A==","signatures":[{"sig":"MEQCIAcD/Mfj5DjJZ5maakShWnueRDesSGdGqfXex9M0a+YCAiBsjRFO3qAjEodEaRo5GLrc+2f2gvUo0VBU0EYXhm6zFA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":60036},"main":"dist/index","types":"./dist/index.d.ts","engines":{"node":">=16"},"gitHead":"e0a45e81cf89922920c80672bad14c0d74ce025c","scripts":{"test":"jest","build":"yarn clean && tsc","clean":"rimraf dist","start":"yarn build"},"_npmUser":{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"},"repository":{"url":"git+https://github.com/amwpcn/amwpcn.git","type":"git"},"_npmVersion":"10.7.0","description":"### TODO:","directories":{},"_nodeVersion":"20.15.1","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","rimraf":"^6.0.1","ts-jest":"^29.2.3","typescript":"^5.5.3","@types/jest":"^29.5.12","@types/node":"^20.14.11"},"_npmOperationalInternal":{"tmp":"tmp/step_0.0.2_1722011537924_0.9952422977891906","host":"s3://npm-registry-packages"}},"0.0.3":{"name":"@amwpcn/step","version":"0.0.3","author":{"name":"Achintha Weerasinghe","email":"achinthamadumal@gmail.com"},"license":"MIT","_id":"@amwpcn/step@0.0.3","maintainers":[{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"}],"homepage":"https://github.com/amwpcn/amwpcn","bugs":{"url":"https://github.com/amwpcn/amwpcn/issues"},"dist":{"shasum":"13f274b97f9cf80fbebb213b4d593afb3658496e","tarball":"https://registry.npmjs.org/@amwpcn/step/-/step-0.0.3.tgz","fileCount":39,"integrity":"sha512-RfVk5nAfNoiWCWexQz5Ipq5LP2gXly9G62CE0u+Re0mViJPECSyv8QMdrD76gBc/OMflhV7lfoErdwTU7gN04A==","signatures":[{"sig":"MEUCIGFFReAr1CUgHUFC06FlumV0iIwycvtFLlHU8XpHtbeAAiEAt351Bl+M9FEapmoM5lwnhJVqIPIN6hyGZ+M2LMv5vjU=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":81640},"main":"dist/index","types":"./dist/index.d.ts","engines":{"node":">=16"},"gitHead":"d800d64bb4fc32ae0042152cb38da1017897bdd8","scripts":{"test":"jest","build":"yarn clean && tsc","clean":"rimraf dist","start":"yarn build"},"_npmUser":{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"},"repository":{"url":"git+https://github.com/amwpcn/amwpcn.git","type":"git"},"_npmVersion":"10.7.0","description":"[![npm version](https://img.shields.io/npm/v/@amwpcn/step)](https://www.npmjs.com/package/@amwpcn/step) [![npm latest](https://img.shields.io/npm/v/@amwpcn/step/latest)](https://www.npmjs.com/package/@amwpcn/step) [![License: MIT](https://img.shields.io/b","directories":{},"_nodeVersion":"20.15.1","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","rimraf":"^6.0.1","ts-jest":"^29.2.3","typescript":"^5.5.3","@types/jest":"^29.5.12","@types/node":"^20.14.11"},"_npmOperationalInternal":{"tmp":"tmp/step_0.0.3_1722154750088_0.7848781420958493","host":"s3://npm-registry-packages"}},"0.0.4":{"name":"@amwpcn/step","version":"0.0.4","keywords":["workflow","step","steps","concurrency","task","pipeline","execution","orchestration"],"author":{"name":"Achintha Weerasinghe","email":"achinthamadumal@gmail.com"},"license":"MIT","_id":"@amwpcn/step@0.0.4","maintainers":[{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"}],"homepage":"https://github.com/amwpcn/amwpcn","bugs":{"url":"https://github.com/amwpcn/amwpcn/issues"},"dist":{"shasum":"5b3e664f661c4906081bb579d251341612aafb93","tarball":"https://registry.npmjs.org/@amwpcn/step/-/step-0.0.4.tgz","fileCount":39,"integrity":"sha512-8GERYvPJWe8seJwzEDzJdxLzM6aoGo4j002D38n+Oabu+DpDY2wilAkEeM3MmhcGubXFMM399xzQkO1q48hCyw==","signatures":[{"sig":"MEUCIQCBvLUHabK/t4KWUdHk8F66UoZ9eBv5Z+SD0oE7lgWRjQIgUMs5VmRly3P6RsRp/j9V335ESyRnqMaUJjCZ2RKvlY0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":84877},"main":"dist/index","types":"./dist/index.d.ts","engines":{"node":">=16"},"gitHead":"b92b44469d97d0884b9d4c2cf6971e5c1c91f53b","scripts":{"test":"jest","build":"yarn clean && tsc","clean":"rimraf dist","start":"yarn build"},"_npmUser":{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"},"repository":{"url":"git+https://github.com/amwpcn/amwpcn.git","type":"git"},"_npmVersion":"10.7.0","description":"A framework for defining and executing steps with hooks and concurrency management","directories":{},"_nodeVersion":"20.15.1","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","rimraf":"^6.0.1","ts-jest":"^29.2.3","typescript":"^5.5.3","@types/jest":"^29.5.12","@types/node":"^20.14.11"},"_npmOperationalInternal":{"tmp":"tmp/step_0.0.4_1722258288057_0.45379980547081256","host":"s3://npm-registry-packages"}},"0.0.5":{"name":"@amwpcn/step","version":"0.0.5","description":"A framework for defining and executing steps with hooks and concurrency management","author":{"name":"Achintha Weerasinghe","email":"achinthamadumal@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/amwpcn/amwpcn.git"},"homepage":"https://github.com/amwpcn/amwpcn","keywords":["workflow","step","steps","concurrency","task","pipeline","execution","orchestration"],"main":"dist/index","engines":{"node":">=16"},"scripts":{"clean":"rimraf dist","build":"yarn clean && tsc","start":"yarn build","test":"jest"},"devDependencies":{"@types/jest":"^29.5.12","@types/node":"^20.14.11","jest":"^29.7.0","rimraf":"^6.0.1","ts-jest":"^29.2.3","typescript":"^5.5.3"},"_id":"@amwpcn/step@0.0.5","gitHead":"3c42ccf2653a2d51ff397c4cfd9d271dcd9d7c19","types":"./dist/index.d.ts","bugs":{"url":"https://github.com/amwpcn/amwpcn/issues"},"_nodeVersion":"20.16.0","_npmVersion":"10.8.1","dist":{"integrity":"sha512-Oo4U7KzVMd02R4zwyWJuwBeMaXL+5d/sdMQxfMYTpzup7iQG5OBv+D5cp5fpaa381rie2oB38eWK4WQj6S5GFw==","shasum":"edeccc6d358fbd779f7307a4c4238c92a89461a7","tarball":"https://registry.npmjs.org/@amwpcn/step/-/step-0.0.5.tgz","fileCount":39,"unpackedSize":88979,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICv3RtRgARYGtDIfGShqqs/Mxeo93j3bItn+En03bP8fAiEAgWDvc799ZQVhKsZhFu1YYNl3BRj/czVSGf8FD8YmAys="}]},"_npmUser":{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"},"directories":{},"maintainers":[{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/step_0.0.5_1722693777509_0.43810902106381877"},"_hasShrinkwrap":false}},"time":{"created":"2024-07-25T16:51:36.497Z","modified":"2024-08-03T14:02:57.880Z","0.0.1":"2024-07-25T16:51:36.737Z","0.0.2":"2024-07-26T16:32:18.130Z","0.0.3":"2024-07-28T08:19:10.306Z","0.0.4":"2024-07-29T13:04:48.226Z","0.0.5":"2024-08-03T14:02:57.675Z"},"bugs":{"url":"https://github.com/amwpcn/amwpcn/issues"},"author":{"name":"Achintha Weerasinghe","email":"achinthamadumal@gmail.com"},"license":"MIT","homepage":"https://github.com/amwpcn/amwpcn","keywords":["workflow","step","steps","concurrency","task","pipeline","execution","orchestration"],"repository":{"type":"git","url":"git+https://github.com/amwpcn/amwpcn.git"},"description":"A framework for defining and executing steps with hooks and concurrency management","maintainers":[{"name":"achinthamadumal","email":"achinthamadumal@gmail.com"}],"readme":"[![npm version](https://img.shields.io/npm/v/@amwpcn/step)](https://www.npmjs.com/package/@amwpcn/step)\n[![npm latest](https://img.shields.io/npm/v/@amwpcn/step/latest)](https://www.npmjs.com/package/@amwpcn/step)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n# Step\n\nThe Step package provides a framework for defining and executing steps with\nbefore and after hooks, and concurrency management.\n\n## Table of Contents\n\n- [Use Cases](#use-cases)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Creating Steps](#creating-steps)\n  - [Chaining Steps](#chaining-steps)\n  - [Executing Steps](#executing-steps)\n  - [Updating Context](#updating-context)\n  - [Graphs](#graphs)\n- [API Reference](#api-reference)\n  - [Step Class](#step-class)\n  - [StepExecutor Class](#stepexecutor-class)\n- [License](#license)\n\n## Use Cases\n\nThe Step library is designed to simplify complex workflows by breaking them down\ninto manageable steps. It is ideal for replacing intricate database triggers\nwith more readable application-level transactions. Use it for automation\nworkflows, data validation, and enrichment processes, or to orchestrate\nmicroservices interactions. It excels in managing business processes like order\nprocessing and approval workflows, handling event-driven architectures. Whether\nyou're developing ETL pipelines, implementing saga patterns for distributed\ntransactions, or designing modular API request handling, the Step library\nprovides a clear and efficient framework for managing sequential and parallel\ntasks.\n\n## Installation\n\nTo install the Step package, run:\n\n```bash\nnpm install @amwpcn/step\n```\n\nor for `yarn`, run:\n\n```bash\nyarn add @amwpcn/step\n```\n\n## Usage\n\n### Creating Steps\n\nThere are several ways to create a Step. You can directly extend the abstract\n`Step` class, create an object with the type of `IStep` interface or to\nimplement `IStep` to your own class. If you do not directly extend the abstract\n`Step` class, you must use `step()` function to create a step. You decide what's\nbest for you. You could always refer the examples define in the repository for\nmore. Here are the examples for each approach.\n\n1. Extend abstract `Step` class\n\n   ```typescript\n   import { IContext, IHandlers, Step } from '@amwpcn/step';\n   import { simulateAsyncTask } from '../helpers';\n   import { updateDocumentCount } from './update-document-count.step';\n\n   interface ImportDocumentContext extends IContext {}\n\n   export class ImportDocumentStep extends Step<ImportDocumentContext> {\n     // Name for your step\n     readonly name: string = 'ImportDocument';\n\n     async execute(\n       context: Readonly<ImportDocumentContext>,\n       handlers: IHandlers<ImportDocumentContext>,\n     ): Promise<void | Step<IContext>[] | Step<IContext>> {\n       // your async task goes here\n\n       // If needed, you could return another step chaining to this step\n       return updateDocumentCount();\n     }\n   }\n\n   // This is just a factory function for your Step. So you don't have to\n   // repeat `Step` postfix or `new` keyword each time you want an instance.\n   export function importDocument() {\n     return new ImportDocumentStep();\n   }\n   ```\n\n2. Create an object of type `IStep`\n\n   ```typescript\n   import { IContext, IStep, step } from '@amwpcn/step';\n   import { simulateAsyncTask } from '../helpers';\n   import { updateDocumentCount } from './update-document-count.step';\n\n   interface DeleteDocumentContext extends IContext {}\n\n   const deleteDocumentStep: IStep<DeleteDocumentContext> & {\n     myCustomDuration?: number;\n     myCustomResult?: string;\n   } = {\n     async prepare(context) {\n       this.myCustomDuration = Math.round(Math.random() * 901 + 100); // Random number between 100 - 1000\n     },\n     async execute(context, handlers) {\n       console.log(`This task is gonna execute: ${this.myCustomDuration}ms`);\n       await simulateAsyncTask(this.myCustomDuration);\n\n       this.myCustomResult = 'Execution went really good!';\n\n       return updateDocumentCount();\n     },\n     async final(context) {\n       console.log(this.myCustomResult);\n     },\n   };\n\n   export function deleteDocument() {\n     return step('DeleteDocument', deleteDocumentStep);\n   }\n   ```\n\n3. Implement `IStep` into your own class\n\n   ```typescript\n   import { IContext, IHandlers, IStep, step, Step } from '@amwpcn/step';\n   import { simulateAsyncTask } from '../helpers';\n\n   interface NotificationContext extends IContext {}\n\n   class NotificationStep implements IStep<NotificationContext> {\n     private _myCustomDuration: number = 500;\n     private _myCustomResult: string = '';\n\n     async prepare(context: Readonly<NotificationContext>): Promise<void> {\n       this._myCustomDuration = Math.round(Math.random() * 901 + 100);\n     }\n\n     async execute(\n       context: Readonly<NotificationContext>,\n       handlers: IHandlers<NotificationContext>,\n     ): Promise<\n       void | Step<NotificationContext> | Step<NotificationContext>[]\n     > {\n       await simulateAsyncTask(this._myCustomDuration);\n\n       this._myCustomResult = 'Execution was successful!';\n     }\n\n     async final(context: Readonly<NotificationContext>): Promise<void> {\n       console.log(this._myCustomResult);\n     }\n   }\n\n   export function notification() {\n     return step('Notification', new NotificationStep());\n   }\n   ```\n\n### Chaining Steps\n\nSteps can be chained using the `enqueueBefore` and `enqueueAfter` methods. If\nyou know the order of steps that needs to be executed ahead of time, you can\ndefine the order like below.\n\n```typescript\nconst deleteStep = stepA()\n  .enqueueBefore(stepB(), 0)\n  .enqueueAfter(stepC(), 1)\n  .enqueueAfter(stepD(), 2);\n```\n\nOr you could add steps dynamically within the `execute` function. These returned\nsteps will be executed immediately even before executing anything in the after\nqueue.\n\n```typescript\nasync execute(context, handlers): Promise<Step<IContext>> {\n   // Execution logic goes here\n\n   if (something === true) {\n      return stepA()\n   }\n\n   return [stepB(), stepC()];\n}\n```\n\nOr if you are directly extending the abstract `Step` class\n\n```typescript\nasync execute(context, handlers): Promise<Step<IContext>> {\n   // Execution logic goes here\n\n   if (something === true) {\n      // Note that enqueueBefore() does not make sense here, since it's already been executed\n      // before coming to execute stage. But you could use enqueueBefore in prepare stage.\n      // But it's not recommended to do anything that affects the execution flow within any other\n      // stage than the `execute`\n      this.enqueueAfter(stepA(), 0);\n   }\n\n   return stepB();\n}\n```\n\n### Executing Steps\n\nUse `createExecutor` to get an instance of the `StepExecutor`. You can pass the\nstep or steps, initial context, error handlers and other options to this\nfunction. Once you get the `StepExecutor` instance, you start the execution.\n\n```typescript\nimport { StepExecutor } from '@amwpcn/step';\n\nconst context = {}; // Your initial context\nconst errorHandlers = {\n  // By returning true: execution of all steps will immediately be stopped.\n  execute(error, stepName) {\n    console.error(stepName, error);\n    return true;\n  },\n  // By returning false: execution will continue despite the error.\n  final(error, stepName) {\n    console.warn(stepName, error);\n    return false;\n  },\n}; // error handlers for each stage\n\nconst executor = createExecutor(deleteStep, {}, errorHandlers, {\n  graph: { enable: true },\n  maxRepetitions: 2,\n  concurrency: {\n    limit: 1,\n  },\n});\n\nexecutor\n  .start()\n  .then(() => {\n    console.log('Execution completed');\n  })\n  .catch((error) => {\n    console.error('Execution failed', error);\n  });\n```\n\nIf you do not define error handlers, the default error handler will kick in and\nimmediately log and stop the execution.\n\n### Updating Context\n\nEach stage handler (prepare, execute, final) will get `handlers` as the second\nparameter which contains some helpers to manage the execution. It has a helper\ncalled `contextUpdater`. Here is an example usage.\n\n```typescript\nhandlers.contextUpdater((context) => ({\n  something: `${context.something}+Updated`,\n}));\n```\n\nChanges to the context will appear in the next stage and/or next steps starts\nexecuting. Parallel steps won't see the changes. This is to avoid any\nun-expected side effects.\n\n### Graphs\n\nIf you enable graphs for execution, nodes and edges required for generation of a\ngraph will be available via executor, once you ran the `start()`.\n\n```typescript\nawait executor.start();\nconst graphData = executor.graphData;\n```\n\nYou could use the above graph data to create your graph using any other graph\nlibraries or save it for debugging later as a JSON file. Here is a example graph\ngenerated using vis-network. Note that `executor.graphData` does not return a\nvis-network graph. We created this graph after mapping the returned data to\nvis-network.\n\n![Step Example Graph](https://raw.githubusercontent.com/amwpcn/amwpcn/master/packages/examples/src/step/static/sample-vis-execution-graph.png)\n\nYou could always check the examples in the repository. But here is the code we\nused to generate the above graph.\n\n```typescript\nconst edges = executor.graphData.edges.map((e) => ({\n  from: e.from,\n  to: e.to,\n  label: e.queueOrder,\n  arrows: 'to',\n  smooth: {\n    type: 'dynamic',\n    roundness: 0.5,\n    forceDirection: 'none',\n  },\n  color: 'black',\n}));\nconst nodes = executor.graphData.nodes.map((n) => ({\n  id: n.id,\n  label: n.label,\n  title: n.ancestors?.join(', '),\n  shape: 'box',\n  color: n.isError ? 'pink' : undefined,\n}));\n```\n\n## API Reference\n\n### Step Class\n\nThe `Step` class is the base class for creating steps. Extend this class and\nimplement the following methods:\n\n- `prepare?(context: Readonly<C>): Promise<void>;`: Preparation logic before\n  executing the step. This runs even before the execution of steps in\n  beforeQueue.\n- `execute(context: Readonly<C>, handlers: IHandlers<C>): Promise<void | Step<C>[] | Step<C>>`:\n  Main execution logic for the step.\n- `rollback?(context: Readonly<C>, handlers: IHandlers<C>): Promise<void | Step<C>[] | Step<C>>`:\n  Rollback logic in case of failure.\n- `final?(context: Readonly<C>): Promise<void>`: Finalization logic after step.\n  This runs even after the execution of steps in afterQueue. execution.\n\n#### Methods\n\n- `enqueueAfter(item: Step<C> | Step<C>[], priority: number): this`: Enqueues a\n  step to be executed after the current step.\n- `enqueueBefore(item: Step<C> | Step<C>[], priority: number): this`: Enqueues a\n  step to be executed before the current step.\n\n### StepExecutor Class\n\nThe `StepExecutor` class is responsible for executing the steps in the correct\norder, managing concurrency, and handling errors.\n\n#### Constructor\n\n```typescript\nconstructor(\n   s: Step<C> | Step<C>[],\n   c: C,\n   errorHandlers?: ErrorHandlers,\n   options?: Options,\n)\n```\n\n- `steps: Step<C> | Step<C>[]`: A single step or an array of steps to execute.\n- `context`: The initial context for the execution.\n- `errorHandlers?: ErrorHandlers`: (Optional). This will take 3 optional error\n  handlers for each stage (prepare, execute, final). If you do not define an\n  error handler for a stage, default error handler will log and stop the\n  execution.\n  ```typescript\n   const errorHandlers = {\n      execute: (error: unknown, stepName: string) => boolean;\n   }\n  ```\n- `options?: Options`: (Optional). Other options for execution such as\n  concurrency and graph settings.\n\n#### Methods\n\n- `start(): Promise<void>`: Starts the execution of the steps.\n\n## License\n\nThis project is licensed under the MIT License.\n","readmeFilename":"README.md"}