{"_id":"f-composer","_rev":"2-db905c25abbb450df4478c37a92d263c","name":"f-composer","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"f-composer","version":"1.0.0","description":"A nodejs library for composing tiny functions together in a sequential manner","main":"lib/index.js","scripts":{"build":"tsc -p .","test":"echo \"Error: no test specified\" && exit 1","dev":"nodemon"},"repository":{"type":"git","url":"git+https://github.com/tareksalem/f-composer.git"},"keywords":["grandjs","nodejs","javascript","typescript","workflow","clunky","functional programming","fp","sequence","middlewares","services"],"author":{"name":"Tarek Salem"},"license":"MIT","bugs":{"url":"https://github.com/tareksalem/f-composer/issues"},"homepage":"https://github.com/tareksalem/f-composer#readme","devDependencies":{"@types/node":"^18.11.18","ts-node":"^10.9.1","typescript":"^4.9.4"},"types":"./lib/index.d.ts","_id":"f-composer@1.0.0","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-SSOl7CUNkcns+8G/en4kevZicr3S8VPDDmLprgbYTeTka03HbVXXmmo6VL3HbysGPrJj6r49GAaaHePaFLZ+Jw==","shasum":"c2855c9862d704f89286473bf4e9562da914d691","tarball":"https://registry.npmjs.org/f-composer/-/f-composer-1.0.0.tgz","fileCount":10,"unpackedSize":20398,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF6ssYfMwccE87NcV0rSy0DN9TKhx0WeBvzbf9L1USdgAiBjh7kUpo/ADOO2JjcU9l5stpHsp45dq/fYIBGwL8KaIQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjuxDZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrOlw//Xkj2ZVZ6UirMLxh4U1V7qZ36Qf7ySI+LumgkOugLqbn+RSRD\r\nA8bjF+kxVIp0i4d8CX+MZ/xrOVAwZzrWTo8PNqVbH1SA0WKX0NGiDBHK23/Q\r\nfqwm+YHXiItWLro93CDwHIw3t3IDyW0XL+jeEtZ8IEhWdHFkR55rtN7n4rXZ\r\noLjE3U/2QSQx3VWcLHd/COiqIr1S9SkNyn/7p3HnkKYsm8VNmxKCAOwc9z1E\r\nol5YQDAsLMJPChPeeFrbcmOkBKovx0I/ysJzCDm2X3f3S1VNt5LAhkxRdduX\r\nHW6uqFXjdexXEWr5Hv25expnqvWwpdPblyVsQ3q7MsAxzdeDvnlGiofGQFne\r\nOZaGjuwZCWiwg7SicCYq9wyaz15eweSp5tclTM/AmY17gXnAf0hj7Slp/nF4\r\n9q8eQRxOFspudEJ70NXh9SJ3zku/UmmVN2wd/NNH8w7dQEFJDlAyz3/n54na\r\n81UuCieu3sjzry+KXcBX+JmazgUgL5R63GNbIYYgx1WjwVlEQgZWHSwnivm5\r\n5FALbshLXTp25tEOyqzMubCE7iKxgBflHz/a1QC0598RHofFnMGNyn59mL6T\r\no03aisBMSSDgqwHRMapcDIEowp0GsHuURooUaPNf3opVb4TXuTTdbrwvzPOO\r\nKjz8suFJdrQSY8pbOXL0hL17FMapnnQGJTc=\r\n=f2R5\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"},"directories":{},"maintainers":[{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/f-composer_1.0.0_1673203929682_0.6665511240526198"},"_hasShrinkwrap":false},"1.0.1":{"name":"f-composer","version":"1.0.1","description":"A nodejs library for composing tiny functions together in a sequential manner","main":"lib/index.js","scripts":{"build":"tsc -p .","test":"echo \"Error: no test specified\" && exit 1","dev":"nodemon"},"repository":{"type":"git","url":"git+https://github.com/tareksalem/f-composer.git"},"keywords":["grandjs","nodejs","javascript","typescript","workflow","clunky","functional programming","fp","sequence","middlewares","services"],"author":{"name":"Tarek Salem"},"license":"MIT","bugs":{"url":"https://github.com/tareksalem/f-composer/issues"},"homepage":"https://github.com/tareksalem/f-composer#readme","devDependencies":{"@types/node":"^18.11.18","ts-node":"^10.9.1","typescript":"^4.9.4"},"types":"./lib/index.d.ts","_id":"f-composer@1.0.1","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-tGShEE/M8vTmJNgl1iBmQEOFFE8Qbv60b0a2QRWQH0pd6mAgPRZalmnOEZQcPchISGOSw6SfusIzNbvwInsJDg==","shasum":"5d9496c032a88e1f64118dbd6652086f40a13359","tarball":"https://registry.npmjs.org/f-composer/-/f-composer-1.0.1.tgz","fileCount":10,"unpackedSize":20378,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGJh/7eWTMiHIK3huiMEGzmARpjIJi/jj7ThnjVmyyVgAiArqcZbTPNU1n7e9cl9eqE358iJ/5ubJbLM90fkFulJcA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjuxD2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoD/w//d9S/hZyFclB8RLAlJGmz0p01TqQbA53CLygRraOPccdUVT6/\r\nCOPveIRclNceK2ulvF60hhABt/Z5+txpX6IX6Zb91kMUXhgk4BvP+uBNlTvh\r\nylwPyuCdkA+c5pM9ML9G0qyskkivpl9OU6A5jjqKo7zPe97z2q+OrDQtGOdD\r\n9ZQ9Y6XcvLG0pkNwlEr9//GD3eXtRSNuxz8yOVWRknHJLDz9TL5SfWYvNV8R\r\nXK9KxDirVoO3qIb9/osAzh7rbL8le7IooZVW+Da/RpAq7nHDwUJSrVYyMBSw\r\nHW0b53tNPrj/yFBOAiegSR6hBWIb/T80VaZiyteSZKv62ndlXQwwyMeHs4e/\r\nBM1EiJ+pG4GZCGJV7Xgf78fAdVoJAEQKSpqsSV3VzBXVr2VL6//znVUBVRBv\r\niGYui9HSX8a0AkSuFiKDcrD3x3EIO+n6Ryib7IWDz3t/azJ4NQFT81BtohYb\r\ncfytuBXqbaYnMqVdy+EgXSfjfyfPRglKzlPkfP9XoUimHu9L97wWltyrAcav\r\nzMWIrp9KfFsC9uozWOcKb1uF/MYfr+npCJaSSzixojNMGUfo+BF6+UUUvUu9\r\nDkm7Aa4rzY7gK7rGe27UzVXCXIuJrSBQeqnFQgdKnD/KoywGnKOzgQE/RlD4\r\nXpVMN7KV5elDV1ImDtfTWJrYxjCSs11hkTw=\r\n=VNRx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"},"directories":{},"maintainers":[{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/f-composer_1.0.1_1673203958023_0.9830463946032584"},"_hasShrinkwrap":false},"1.0.2":{"name":"f-composer","version":"1.0.2","description":"A nodejs library for composing tiny functions together in a sequential manner","main":"lib/index.js","scripts":{"build":"tsc -p .","test":"echo \"Error: no test specified\" && exit 1","dev":"nodemon"},"repository":{"type":"git","url":"git+https://github.com/tareksalem/f-composer.git"},"keywords":["grandjs","nodejs","javascript","typescript","workflow","clunky","functional programming","fp","sequence","middlewares","services"],"author":{"name":"Tarek Salem"},"license":"MIT","bugs":{"url":"https://github.com/tareksalem/f-composer/issues"},"homepage":"https://github.com/tareksalem/f-composer#readme","devDependencies":{"@types/node":"^18.11.18","ts-node":"^10.9.1","typescript":"^4.9.4"},"types":"./lib/index.d.ts","gitHead":"316f2da7659431438d83b74fe326d5cdbc6ee6df","_id":"f-composer@1.0.2","_nodeVersion":"18.13.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-lIfNtItJfCckTPiGxyE2PSX31bdHVjKetgbDR8hTND7JQh7EMFKC6BmbRsipNEil6HYNeTzmngq4gdwjjMEt5A==","shasum":"e308b3b033fb6a237f268837b865e99fa0f9759b","tarball":"https://registry.npmjs.org/f-composer/-/f-composer-1.0.2.tgz","fileCount":14,"unpackedSize":31075,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCXnvzhl8IIi08M5tqHsZJBkMjuTa4SR06qYRFKubQYAIgSO7ps76Tvg3EbtsQW6jQsPco0+8xseMXlIByfrhrbO4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjwxc+ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmosRQ/6AwKAnJJao7+qFILZkcZH/697adkDBhKsDhysRIX8IINsoSQD\r\nADcoVI+a+SZDQywzbLzgsFUQsMI4nV8QPVxXDiJN0iv01a13snDg6SINCSPZ\r\nrPEmElxsckv+VXYBWTFCETkAiwltvNl4NarFt+gEakEJ5uG1p3hTpjwVS5Xv\r\nLf/8DKNLHjSPOi6VXXwX3fPbT1v6YGTHIybTJFnLnqH8pZDehTLSy5Wcljhw\r\naI6vTF82ae/Dks/srbpAFsyiz0c48C7zqplH1+Cf1+SG73HWdBA9FJxXKyay\r\nwkY7Iq33Q8xSX503srI+OeAqIokNMw7SaA9q0Rs1fURLfvbcJIa/13nFGLWb\r\nwspzhpuEnmSjnqnT/GbeX/RSmvXnsjJTcAG+Zf/lqAf4z32pr0FXbpyirRq+\r\nF7WX3gi8Ic+TF/ZBOzf3mokzZNC9Hv9TgkiswYtkCEStpSNXTThYG94n738Y\r\nSh7Uurx95msVZsEHCdaPaeqCfpzeTD//iLmRAL6Iuo6iWGNDEompeQf6ZPsP\r\nvU/Z6XrP0kRhbRV1YAD2edcoF8dctNTvQix0kd77lPZtS9wyGTIwm4rxukSk\r\nbr7kCMBJTbKDDRbxtLyMyISrwJu0tywKBrdd9HmHJep2Ki+TJ92PzSWdBJfQ\r\nf02jkB8oYZNnSgXq7EhXSEHcZhq38/J6vaA=\r\n=HuN6\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"},"directories":{},"maintainers":[{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/f-composer_1.0.2_1673729854183_0.6391477821651144"},"_hasShrinkwrap":false}},"time":{"created":"2023-01-08T18:52:09.681Z","1.0.0":"2023-01-08T18:52:09.855Z","modified":"2023-01-14T20:57:34.526Z","1.0.1":"2023-01-08T18:52:38.175Z","1.0.2":"2023-01-14T20:57:34.440Z"},"maintainers":[{"name":"tareksalem1","email":"tareksalem19982018@gmail.com"}],"description":"A nodejs library for composing tiny functions together in a sequential manner","homepage":"https://github.com/tareksalem/f-composer#readme","keywords":["grandjs","nodejs","javascript","typescript","workflow","clunky","functional programming","fp","sequence","middlewares","services"],"repository":{"type":"git","url":"git+https://github.com/tareksalem/f-composer.git"},"author":{"name":"Tarek Salem"},"bugs":{"url":"https://github.com/tareksalem/f-composer/issues"},"license":"MIT","readme":"# F-Composer\n\n\nA nodejs library for composing tiny functions together in a sequential manner\n\n## Installation\n\n```cmd\nnpm i f-composer\n```\n\n## Introduction\n\nThis library has a backbone component which is `Workflow`, basically the workflow is a collection of tiny functions are combined together to form a bigger implementation, these tiny functions are called steps, these steps are exeucted in a sequential manner, you can also inject some hooks for each steps to be exeucted before or after the step execution\n\nThis library borrows some concepts from functional programming, one of these concepts, each step added to this workflow should have input or output, and each output of the function will be the input for the next one.\n\n## Use Case\n- **FP Implementation**: In javascript world you have the ability to write your code in different paradigms, and different ways, one of these paradigms is expressing your code in functions, could be anonymous functions, named functions or arrow functions, using this library you can expose your busines in very small functions and compose them together in workflow to build something bigger that can execute these tiny functions in a sequential manner\n- **Decorators**: If you are familiar with OOP world you will be familiar with decorators pattern to attach additional behaviors to your code without affecting the code base, using this library you can do something similar to this pattern, by attaching hooks to the workflow steps you can inject additional behaviors to be executed before or after each step in your workflow\n- **Dependency Injection**: The hooks concept in this library could be used to inject things into your functions without affecting these functions or reading things from the global scope\n- **Middlewares**: In http routing world, there is a common concept is called middleware which allows you to execute some behaviors without affecting the main context, the problem is, this concept is only used on http layer or router later or controller layer, but doesn't exist on service layer, this library tries to bring this concept to the down layer, which is the service layer, so instead of putting your logic inside a single function or inside a single service, you can start divide the service implementation into smaller functions that could be executed in a sequential manner\n\n## Concept\n\n![f-composer concept](./f-composer-concept.svg)\n\nThe concept of workflows is about grouping multiple and related functionalities and compose them in one method, suppose you have a function like place an order as the following:\n\n```typescript\nfunction placeOrder() {\n    createOrder()\n    doPayment()\n    clearCart()\n    sendEmail()\n}\n```\n\nIn this example, you see you have a big functionality that knows about everything related to creating and order, doing payment, clearing cart and sending emails, and you are calling these method in a sequence, so instead of calling these methods in that way in a bigger function, you just create a workflow and start adding the steps and the library will compose these functions together\n\nThis library aims to help you devide your code into very small, specific functions that do one thing, based on single responsibility principle, this will help you to make your code more testable and extendable\n\n\n## Usage\n\nThis library could be used in typescript or javascript, also can run either on nodejs server or on browser\n\n```typescript\nconst workflow = Workflow.NewWorkflow();\n```\n\n`NewWorkflow` method is used to create new workflow object\n\nThen you can add steps as much as you want, you can add one step using the following method\n\n```typescript\nworkflow.addStep(step, ...hooks);\n```\n\nThis method takes endless arguments, the first argument is an object, which represents the actual step that needs to be executed and the rest of arguments are hooks that can be executed `before` or `after` the step execution\n\n**Step Fields**\n\n| field   | value type | required | description                                                         |\n|---------|------------|----------|---------------------------------------------------------------------|\n| func    | function   | true     | the function that should be executed in this step                   |\n| options | object     | false    | this is an optional object contains some configuration for the step |\n\n**Options Object**\n\n| field                 | value type | required | default value | description                                                                                                                                |\n|-----------------------|------------|----------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------|\n| readFromOriginalInput | boolean    | false    | false         | if sat to true, it will read from the original input not from the output of the previous step                                              |\n| defaultReturningInput | boolean    | false    | false         | if sat to true and there is no result returned from this step, the workflow will automatically return the input of this step as its result |\n\n**Here is an example for step object**\n\n```typescript\nworkflow.addStep({\n    func: () => {\n        // here is logic for step to be executed\n    },\n    options: {\n        readFromOriginalInput: false,\n        defaultReturningInput: false\n    }\n});\n```\n\n**Note**\neach step function can return an output, this output will be used to feed the next function input\n\n\n#### Adding multiple steps in one call\n\nyou can add multiple steps in one call using the following method:\n\n```typescript\nworkflow.addSteps([step, ...hooks], [step, ...hooks]);\n```\n\nthis method takes endless arguments, each argument should be an array with at least one argument, the first argument is the step that you want to execute, and the other arguments are optional, each one could be an object represents a hook that should be executed before or after executing the actual step, please refer to `addStep` method above\n\n**Note**\nthe functions are executed sequentially and in FIFO mode, which means the first function registered to this workflow it will be the first one executed\n\n### Building the workflow\nAfter adding steps to the workflow now you need to compose/build the workflow you created, by combining the steps in a series, this is done using a function called `composeAsync`, if you are working with typescript, this function takes a generic type which is the returned type from this workflow\n\n```typescript\nworkflow.composeAsync<string>();\n```\n\n### Hooks\n\nWhile you can add multiple steps to be executed in a sequence, you can also use something called `hooks` that will be attached to each step in the workflow\n\nSuppose you have the following scenario:\n\nyou have three steps, and each step, and each step does specific action and you want to log the input and the output for each step, in this case you can attach hooks which are type of functions that can be executed before executing the step or after executing it\n\n**Example**\n\n```typescript\nconst workflow = Workflow.NewWorkflow()\nconst stepOne = () => {\n    // do implementation for step one here\n}\nconst stepTwo = () => {\n    // do implementation for step two here\n}\n\nconst loggerHook = (inp) => {\n    console.log('=============== logger', inp);\n    return inp;\n}\n\nworkflow.addStep({\n    func: stepOne,\n}, {\n    type: HookType.before,\n    func: loggerHook,\n})\nworkflow.addStep({\n    func: stepTwo,\n}, {\n    type: HookType.before,\n    func: loggerHook\n})\n```\nEach hook is an object that has multiple fields, `type` which is the hook type, `func` field which is the function that will be executed as a hook, and `options` which is some options to decide the hook behavior\n\n**Step Fields**\n\n| field   | value type | required | description                                                         |\n|---------|------------|----------|---------------------------------------------------------------------|\n| type    | `before`, `after`, `workflowError`, `stepError`   | true     | the type of the hook to be executed before or after the step execution                   |\n| func    | function   | true     | the function that should be executed in this step                   |\n| options | object     | false    | this is an optional object contains some configuration for the step |\n\n**Options Object**\n\n| field                 | value type | required | default value | description                                                                                                                                |\n|-----------------------|------------|----------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------|\n| readFromOriginalInput | boolean    | false    | false         | if sat to true, it will read from the original input not from the output of the previous step                                              |\n| defaultReturningInput | boolean    | false    | false         | if sat to true and there is no result returned from this hook, the workflow will automatically return the input of this hook as its result |\n\n**Note the following**\n\n- Hooks with `before` type will change the inputs of the step\n- `before` hooks are executed in a sequence with FIFO order before executing the step function\n- `after` hooks are executed in setImmediate mode to enable the workflow to execute the rest of steps\n- `after` hooks doesn't affect other steps inputs\n- `defaultReturningInput` this option doesn't take effect on hooks with `after` type\n\n#### Hooks use cases\n\nyou can use hooks to do the following \"not limited to\":\n\n- do logging before and after the step execution\n- do dependency injection before executing the workflow step\n- attach additional behaivour with `after` type to the step without affecting the main workflow like sending emails or notifications\n- do something like check access permissions or privileges before executing the step function \n\n## Examples\n\n### Logger Example\n\nThis example shows you how to attach a logger before executing the workflow steps and after executing them\n\n```typescript\nconst workflow = Workflow.NewWorkflow();\n\nconst startLoggerStep = (...args: any) => {\n    console.log('started executing workflow', args);\n    return args;\n};\n\nconst endLoggerStep = (result: number) => {\n    console.log('ending executing workflow', result);\n    return result;\n};\n\nconst add1Step = (num: number): number => {\n    return num + 1;\n}\n\nconst adder1Service = workflow.addStep(startLoggerStep).addStep(add1Step).addStep(endLoggerStep).composeAsync<number>();\n\n(async () => {\n    const result = await adder1Service(1)\n    console.log(result);\n})()\n\n```\n","readmeFilename":"README.md"}