{"id":13580004,"url":"https://github.com/danielduarte/flowed","last_synced_at":"2025-10-25T17:33:25.773Z","repository":{"id":39871908,"uuid":"183160503","full_name":"danielduarte/flowed","owner":"danielduarte","description":"A fast and reliable flow engine for orchestration and more uses in Node.js, Deno and the browser","archived":false,"fork":false,"pushed_at":"2025-02-26T18:00:02.000Z","size":1920,"stargazers_count":169,"open_issues_count":10,"forks_count":22,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-03T17:09:39.419Z","etag":null,"topics":["flow","flow-engine","flow-execution","flow-specifications","flowengine","flowframework","parallel","process","task-manager","task-runner","workflow"],"latest_commit_sha":null,"homepage":"https://danielduarte.github.io/flowed","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielduarte.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-04-24T06:14:44.000Z","updated_at":"2025-03-25T17:46:54.000Z","dependencies_parsed_at":"2024-01-16T10:36:01.875Z","dependency_job_id":"6d7e5f74-15df-47a0-b75b-674318cdddbe","html_url":"https://github.com/danielduarte/flowed","commit_stats":{"total_commits":214,"total_committers":2,"mean_commits":107.0,"dds":0.03738317757009346,"last_synced_commit":"094ba1598435b5cf6c15a7cf906960078e79fed9"},"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielduarte%2Fflowed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielduarte%2Fflowed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielduarte%2Fflowed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielduarte%2Fflowed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielduarte","download_url":"https://codeload.github.com/danielduarte/flowed/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501861,"owners_count":21114684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flow","flow-engine","flow-execution","flow-specifications","flowengine","flowframework","parallel","process","task-manager","task-runner","workflow"],"created_at":"2024-08-01T15:01:45.938Z","updated_at":"2025-10-25T17:33:20.748Z","avatar_url":"https://github.com/danielduarte.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\u003ca href=\"https://danielduarte.github.io/flowed/\"\u003e\u003cimg width=\"445\" height=\"100\" src=\"./doc/flowed-logo.svg\" alt=\"Flowed Logo\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003eA fast and reliable flow engine for orchestration and more uses in \u003cem\u003eNode.js\u003c/em\u003e, \u003cem\u003eDeno\u003c/em\u003e and the browser.\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/danielduarte/flowed/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/flowed?color=%23007ec6\" alt=\"License\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://www.npmjs.com/package/flowed\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/flowed\" alt=\"NPM Package Version\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://app.fossa.com/attribution/e587cd9b-f7f8-4fa6-88b1-f81832d9ce07\"\u003e\u003cimg src=\"https://app.fossa.com/api/projects/custom%2B13599%2Fgithub.com%2Fdanielduarte%2Fflowed.svg?type=shield\" alt=\"FOSSA Status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://coveralls.io/github/danielduarte/flowed?branch=main\"\u003e\u003cimg src=\"https://coveralls.io/repos/github/danielduarte/flowed/badge.svg?branch=main\" alt=\"Coverage Status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://sonarcloud.io/dashboard?id=danielduarte_flowed\"\u003e\u003cimg src=\"https://sonarcloud.io/api/project_badges/measure?project=danielduarte_flowed\u0026metric=alert_status\" alt=\"Quality Gate Status\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n## Installation\n\n```\nnpm i flowed\n```\n\n## Browser\n\n**From public CDN**\n\n```HTML\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/flowed@latest/dist/lib/flowed.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n```\n\nOr replace `latest` in the URL for your desired version.\n\n**From package**\n\n```HTML\n\u003cscript src=\"./dist/lib/flowed.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n```\n\n## Getting Started\n\n- [Tutorial](./doc/tutorial.md)\n- [Code examples](https://github.com/danielduarte/flowed/tree/main/test/examples)\n\n\n## Main Features\n\n- [Parallel execution](#parallel-execution)\n- [Dependency management](#dependency-management)\n- [Asynchronous and synchronous tasks](#asynchronous-and-synchronous-tasks)\n- [JSON based flows](#json-based-flow-specifications)\n- [Parametrized running](#parametrized-running)\n- [Scoped visibility for tasks](#scoped-visibility-for-tasks)\n- [Run flows from string, object, file or URL](#run-flows-from-string-object-file-or-url)\n- [Pause/Resume and Stop/Reset functions](#pauseresume-and-stopreset-functions)\n- [Inline parameters transformation](#inline-parameters-transformation)\n- [Cyclic flows](#cyclic-flows)\n- [Library with reusable frequently used tasks](#library-with-reusable-frequently-used-tasks)\n- [Plugin system](#plugin-system)\n- [Debugging](#debugging)\n\n\n### Parallel execution\n\nIn order to run tasks in parallel, you don't have to do anything.\nSimply adding them to a flow, they will run in parallel, of course if they don't have dependence on each other.\n\n\n![Parallel Tasks](./doc/example-parallel.png)\n\n```JavaScript\nconst flow = {\n  tasks: {\n    A: {\n      provides: ['resultA'],\n      resolver: {\n        name: 'flowed::Noop',\n      },\n    },\n    B: {\n      provides: ['resultB'],\n      resolver: {\n        name: 'flowed::Noop',\n      },\n    },\n    C: {\n      requires: ['resultA', 'resultB'],\n      resolver: {\n        name: 'flowed::Noop',\n      },\n    },\n  },\n}\n```\n\n```JavaScript\nFlowManager.run(flow);\n```\n\n\n### Dependency management\n\nOnly specifying dependent inputs and outputs the flow manages dependencies automatically, executing in the correct order,\nmaximizing parallelism, and at the same time waiting for expected results when required.\nNote that you can specify dependence between tasks arbitrarily, not only when one of them needs results from the other.\n\n![Dependent Tasks](./doc/example-dependent.png)\n\n```JavaScript\nconst flow = {\n  tasks: {\n    A: {\n      provides: ['resultA'],\n      resolver: {\n        name: 'flowed::Noop',\n      },\n    },\n    B: {\n      requires: ['resultA'],\n      provides: ['resultB'],\n      resolver: {\n        name: 'flowed::Noop',\n      },\n    },\n    C: {\n      requires: ['resultB'],\n      resolver: {\n        name: 'flowed::Noop',\n      },\n    },\n  },\n}\n```\n\n```JavaScript\nFlowManager.run(flow);\n```\n\n\n### Asynchronous and synchronous tasks\n\nEach task in a flow is associated to a \"resolver\", which is the piece of code that runs when the task executes.\nSo, resolvers resolve the goals of tasks.\nWell, those resolvers can be synchronous (simply returning its results) or asynchronous (returning a promise).\nThe flow will take care of promises, waiting for results according to dependencies. \n\n\n### JSON based flow specifications\n\nThe flow specifications are written in JSON format, which means:\n- They are easily serialized/unserialized for storage and transmission.\n- They are developer and IDE friendly, very easy to read and understand.\n- They are easily or actually directly transformable from and to JavaScript objects.\n- They are actually supported for most modern programming languages.\n- They are API friendly.\n\nOr in a few words, JSON specs means saving a lot of time and headaches.\n\n\n### Parametrized running\n\nThe same flow can be ran many times with different parameters provided from outside.\nParameters can satisfy requirements for tasks that expect for them, in the same way they can be provided as\noutput of another task.\nThat also means, if you already have pre-calculated results, you can provide them to the flow and accelerate its execution.\n\n\n### Scoped visibility for tasks\n\nAll tasks in a flow, even the ones that have the same resolver, have a private space of names for parameters and results.\nThat means:\n- When developing a new resolver, the developer don't have take care of name collisions.\n- Resolvers are always reusable between flows.\n\nBut, what happends with resourses that I want to share all through the flow?\nLoggers, connections, application references, whatever.\nIn that case, you can use a \"context\" that's also available in all the flow tasks.\nAll resources in the contexts are shared.\n\n\n### Run flows from string, object, file or URL\n\nFlow can be ran from a specification provided from a JSON string, a JavaScript object,\na JSON file or an accessible URL that serves the spec in JSON.\n\n\n### Pause/Resume and Stop/Reset functions\n\nFlow executions can be paused and resumed with task granularity.\nThe same for stopping and resetting, that last being to set the flow up to start from the beginning. \n\n\n### Inline parameters transformation\n\nTask parameters can be transformed before running each task using an inline template.\n\nIn this example, given the current date `new Date()`, a simple flow is used to get a plane JavaScript object with day, month and year.\n\nThe template embedded in the flow is:\n\n\u003c!--- {% raw %} --\u003e\n```JavaScript\n{\n  day: '{{date.getDate()}}',\n  month: '{{date.getMonth() + 1}}',\n  year: '{{date.getFullYear()}}'\n}\n```\n\u003c!--- {% endraw %} --\u003e\n\nWhere `date` is known by the template because it is in the `requires` list of the task `convertToObj`.\n\n\u003c!--- {% raw %} --\u003e\n```JavaScript\nconst flow = {\n  tasks: {\n    getDate: {\n      provides: ['date'],\n      resolver: {\n        name: 'flowed::Echo',\n        params: { in: { value: new Date() } },\n        results: { out: 'date' },\n      }\n    },\n    convertToObj: {\n      requires: ['date'],\n      provides: ['result'],\n      resolver: {\n        name: 'flowed::Echo',\n        params: {\n          in: {\n            transform: {\n              day: '{{date.getDate()}}',\n              month: '{{date.getMonth() + 1}}',\n              year: '{{date.getFullYear()}}',\n            }\n          }\n        },\n        results: { out: 'result' },\n      }\n    }\n  },\n};\n```\n\u003c!--- {% endraw %} --\u003e\n\n```JavaScript\nFlowManager.run(flow, {}, ['result']);\n```\n\nThe result got today (01/03/2020) is `{ day: 3, month: 1, year: 2020 }`.\n\nIn order to use the benefits of the template transformation I highly recommend to take a look at [the ST documentation](https://selecttransform.github.io/site/transform.html) and check the features and examples. \nAlso play with [this tool](https://selecttransform.github.io/playground) provided by [ST](https://selecttransform.github.io/site), and design your templates dynamically.\n\n\n### Cyclic flows\n\nFlows can have cyclic dependencies forming loops.\nIn order to run these flows, external parameters must raise the first execution.\n\nTimer example:\n\nThis resolver will check a context value to know if the clock must continue ticking or not.\n\nAlso the current tick number is output to the console.\n\n```JavaScript\nclass Print {\n  exec({ message }, context) {\n    return new Promise((resolve, reject) =\u003e {\n\n      let stop = false;\n      context.counter++;\n      if (context.counter === context.limit) {\n        stop = true;\n      }\n\n      setTimeout(() =\u003e {\n        console.log(message, context.counter);\n        resolve(stop ? {} : { continue: true });\n      }, 1000);\n    });\n  }\n}\n```\n\nAnd the flow execution:\n\n```JavaScript\nflowed.FlowManager.run({\n    tasks: {\n      tick: {\n        requires: ['continue'],\n        provides: ['continue'],\n        resolver: {\n          name: 'Print',\n          params: { message: { value: 'Tick' } },\n          results: { continue: 'continue' },\n        }\n      },\n    },\n  }, { continue: true }, [], { Print }, { counter: 0, limit: 5 },\n);\n```\n\nThe expected console output is:\n\n```\nTick 1\nTick 2\nTick 3\nTick 4\nTick 5\n```\n\nNote that the task requires and provides the same value `continue`.\n\nIn order to solve the cyclic dependency and start running the flow, the first value for `continue` is provided in the parameters `{ continue: true }`. Otherwise, the flow would never start.\n\nThe dependency loops can be formed by any number of tasks, not only by the same as in this simple example.\n\n\n### Library with reusable frequently used tasks\n\nFor several common tasks, resolvers are provided in the bundle, so you don't have to worry about programming the same thing over and over again.\nYou just have to take care of your custom code.\n\nFor more information, please check the [Built-In resolver library documentation](./doc/resolver-library.md)\n\nOr go directly to  the desired resolver details:\n\n- [Noop](./doc/resolver-library.md#noop)\n- [Echo](./doc/resolver-library.md#echo)\n- [ThrowError](./doc/resolver-library.md#throwerror)\n- [Conditional](./doc/resolver-library.md#conditional)\n- [Wait](./doc/resolver-library.md#wait)\n- [SubFlow](./doc/resolver-library.md#subflow)\n- [Repeater](./doc/resolver-library.md#repeater)\n- [ArrayMap](./doc/resolver-library.md#arraymap)\n- [Stop](./doc/resolver-library.md#stop)\n- [Pause](./doc/resolver-library.md#pause)\n\n\n### Plugin system\n\nUsing the Flowed plugin system, any developer can add their own resolver library and easily integrate it into the flow engine.\n\nCustom resolver libraries can even be published and distributed as independent NPM packages.\n\n\n### Debugging\n\n(debugging doc coming soon...)\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielduarte%2Fflowed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielduarte%2Fflowed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielduarte%2Fflowed/lists"}