{"id":14982022,"url":"https://github.com/gulpjs/bach","last_synced_at":"2026-04-02T02:07:50.604Z","repository":{"id":14894327,"uuid":"17618120","full_name":"gulpjs/bach","owner":"gulpjs","description":"Compose your async functions with elegance.","archived":false,"fork":false,"pushed_at":"2023-04-16T20:13:26.000Z","size":61,"stargazers_count":136,"open_issues_count":0,"forks_count":19,"subscribers_count":7,"default_branch":"master","last_synced_at":"2026-03-10T13:53:11.221Z","etag":null,"topics":["async","async-functions","callback","composing-functions","gulpjs","javascript","observable","parallel","promise","series","settled","stream"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/gulpjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"github":["gulpjs","phated","yocontra"],"tidelift":"npm/gulp","open_collective":"gulpjs"}},"created_at":"2014-03-11T04:13:26.000Z","updated_at":"2026-03-03T16:54:28.000Z","dependencies_parsed_at":"2023-11-23T06:37:57.274Z","dependency_job_id":"20745431-a359-42ed-b39b-40aaab1b1516","html_url":"https://github.com/gulpjs/bach","commit_stats":{"total_commits":60,"total_committers":13,"mean_commits":4.615384615384615,"dds":0.2833333333333333,"last_synced_commit":"a825fb2b1a42e6bb1a3bfcd39cb27420ed9fb08a"},"previous_names":["phated/bach"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/gulpjs/bach","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gulpjs%2Fbach","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gulpjs%2Fbach/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gulpjs%2Fbach/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gulpjs%2Fbach/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gulpjs","download_url":"https://codeload.github.com/gulpjs/bach/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gulpjs%2Fbach/sbom","scorecard":{"id":449149,"data":{"date":"2025-08-11","repo":{"name":"github.com/gulpjs/bach","commit":"a825fb2b1a42e6bb1a3bfcd39cb27420ed9fb08a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":1,"reason":"Found 4/28 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/dev.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dev.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/dev.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/dev.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/dev.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dev.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/dev.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dev.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/dev.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/dev.yml:59: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/dev.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/dev.yml:72: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/dev.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/gulpjs/bach/release.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/dev.yml:51","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 third-party GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/gulpjs/.github/SECURITY.md:1","Info: Found linked content: github.com/gulpjs/.github/SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/gulpjs/.github/SECURITY.md:1","Info: Found text in security policy: github.com/gulpjs/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T07:42:05.625Z","repository_id":14894327,"created_at":"2025-08-19T07:42:05.626Z","updated_at":"2025-08-19T07:42:05.626Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31218928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-31T04:08:55.938Z","status":"ssl_error","status_checked_at":"2026-03-31T04:08:47.883Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["async","async-functions","callback","composing-functions","gulpjs","javascript","observable","parallel","promise","series","settled","stream"],"created_at":"2024-09-24T14:04:39.618Z","updated_at":"2026-04-02T02:07:50.574Z","avatar_url":"https://github.com/gulpjs.png","language":"JavaScript","funding_links":["https://github.com/sponsors/gulpjs","https://github.com/sponsors/phated","https://github.com/sponsors/yocontra","https://tidelift.com/funding/github/npm/gulp","https://opencollective.com/gulpjs"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://gulpjs.com\"\u003e\n    \u003cimg height=\"257\" width=\"114\" src=\"https://raw.githubusercontent.com/gulpjs/artwork/master/gulp-2x.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# bach\n\n[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coveralls Status][coveralls-image]][coveralls-url]\n\nCompose your async functions with elegance.\n\n## Usage\n\nWith `bach`, it is very easy to compose async functions to run in series or parallel.\n\n```js\nvar bach = require('bach');\n\nfunction fn1(cb) {\n  cb(null, 1);\n}\n\nfunction fn2(cb) {\n  cb(null, 2);\n}\n\nfunction fn3(cb) {\n  cb(null, 3);\n}\n\nvar seriesFn = bach.series(fn1, fn2, fn3);\n// fn1, fn2, and fn3 will be run in series\nseriesFn(function (err, res) {\n  if (err) {\n    // in this example, err is undefined\n    // handle error\n  }\n  // handle results\n  // in this example, res is [1, 2, 3]\n});\n\nvar parallelFn = bach.parallel(fn1, fn2, fn3);\n// fn1, fn2, and fn3 will be run in parallel\nparallelFn(function (err, res) {\n  if (err) {\n    // in this example, err is undefined\n    // handle error\n  }\n  // handle results\n  // in this example, res is [1, 2, 3]\n});\n```\n\nSince the composer functions return a function, you can combine them.\n\n```js\nvar combinedFn = bach.series(fn1, bach.parallel(fn2, fn3));\n// fn1 will be executed before fn2 and fn3 are run in parallel\ncombinedFn(function (err, res) {\n  if (err) {\n    // in this example, err is undefined\n    // handle error\n  }\n  // handle results\n  // in this example, res is [1, [2, 3]]\n});\n```\n\nFunctions are called with [async-done], so you can return a stream, promise, observable or child process. See [`async-done` completion and error resolution][completions] for more detail.\n\n```js\n// streams\nvar fs = require('fs');\n\nfunction streamFn1() {\n  return fs\n    .createReadStream('./example')\n    .pipe(fs.createWriteStream('./example'));\n}\n\nfunction streamFn2() {\n  return fs\n    .createReadStream('./example')\n    .pipe(fs.createWriteStream('./example'));\n}\n\nvar parallelStreams = bach.parallel(streamFn1, streamFn2);\nparallelStreams(function (err) {\n  if (err) {\n    // in this example, err is undefined\n    // handle error\n  }\n  // all streams have emitted an 'end' or 'close' event\n});\n```\n\n```js\n// promises\nfunction promiseFn1() {\n  return Promise.resolve(1);\n}\n\nfunction promiseFn2() {\n  return Promise.resolve(2);\n}\n\nvar parallelPromises = bach.parallel(promiseFn1, promiseFn2);\nparallelPromises(function (err, res) {\n  if (err) {\n    // in this example, err is undefined\n    // handle error\n  }\n  // handle results\n  // in this example, res is [1, 2]\n});\n```\n\nAll errors are caught in a [domain] and passed to the final callback as the first argument.\n\n```js\nfunction success(cb) {\n  setTimeout(function () {\n    cb(null, 1);\n  }, 500);\n}\n\nfunction error() {\n  throw new Error('Thrown Error');\n}\n\nvar errorThrownFn = bach.parallel(error, success);\nerrorThrownFn(function (err, res) {\n  if (err) {\n    // handle error\n    // in this example, err is an error caught by the domain\n  }\n  // handle results\n  // in this example, res is [undefined]\n});\n```\n\nWhen an error happens in a parallel composition, the callback will be called as soon as the error happens.\nIf you want to continue on error and wait until all functions have finished before calling the callback, use `settleSeries` or `settleParallel`.\n\n```js\nfunction success(cb) {\n  setTimeout(function () {\n    cb(null, 1);\n  }, 500);\n}\n\nfunction error(cb) {\n  cb(new Error('Async Error'));\n}\n\nvar parallelSettlingFn = bach.settleParallel(success, error);\nparallelSettlingFn(function (err, res) {\n  // all functions have finished executing\n  if (err) {\n    // handle error\n    // in this example, err is an error passed to the callback\n  }\n  // handle results\n  // in this example, res is [1]\n});\n```\n\n## API\n\n### `series(fns..., [options])`\n\nTakes a variable amount of functions (`fns`) to be called in series when the returned function is\ncalled. Optionally, takes an [options](#options) object as the last argument.\n\nReturns an `invoker(cb)` function to be called to start the serial execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.\n\nIf all functions complete successfully, the callback function will be called with all `results` as the second argument.\n\nIf an error occurs, execution will stop and the error will be passed to the callback function as the first parameter. The error parameter will always be a single error.\n\n### `parallel(fns..., [options])`\n\nTakes a variable amount of functions (`fns`) to be called in parallel when the returned function is\ncalled. Optionally, takes an [options](#options) object as the last argument.\n\nReturns an `invoker(cb)` function to be called to start the parallel execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.\n\nIf all functions complete successfully, the callback function will be called with all `results` as the second argument.\n\nIf an error occurs, the callback function will be called with the error as the first parameter. Any async functions that have not completed, will still complete, but their results will **not** be available. The error parameter will always be a single error.\n\n### `settleSeries(fns..., [options])`\n\nTakes a variable amount of functions (`fns`) to be called in series when the returned function is\ncalled. Optionally, takes an [options](#options) object as the last argument.\n\nReturns an `invoker(cb)` function to be called to start the serial execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.\n\nAll functions will always be called and the callback will receive all settled errors and results. If any errors occur, the error parameter will be an array of errors.\n\n### `settleParallel(fns..., [options])`\n\nTakes a variable amount of functions (`fns`) to be called in parallel when the returned function is\ncalled. Optionally, takes an [options](#options) object as the last argument.\n\nReturns an `invoker(cb)` function to be called to start the parallel execution. The invoker function takes a callback (`cb`) with the `function(error, results)` signature.\n\nAll functions will always be called and the callback will receive all settled errors and results. If any errors occur, the error parameter will be an array of errors.\n\n### `options`\n\nThe `options` object is primarily used for specifying functions that give insight into the lifecycle of each function call. The possible extension points are `create`, `before`, `after` and `error`. If an extension point is not specified, it defaults to a no-op function.\n\nThe `options` object for `parallel` and `settleParallel` also allows specifying `concurrency` in which to run your functions. By default, your functions will run at maximum concurrency.\n\n##### `options.concurrency`\n\nLimits the amount of functions allowed to run at a given time.\n\n##### `options.create(fn, index)`\n\nCalled at the very beginning of each function call with the function (`fn`) being executed and the `index` from the array/arguments. If `create` returns a value (`storage`), it is passed to the `before`, `after` and `error` extension points.\n\nIf a value is not returned, an empty object is used as `storage` for each other extension point.\n\nThis is useful for tracking information across an iteration.\n\n##### `options.before(storage)`\n\nCalled immediately before each function call with the `storage` value returned from the `create` extension point.\n\n##### `options.after(result, storage)`\n\nCalled immediately after each function call with the `result` of the function and the `storage` value returned from the `create` extension point.\n\n##### `options.error(error, storage)`\n\nCalled immediately after a failed function call with the `error` of the function and the `storage` value returned from the `create` extension point.\n\n## License\n\nMIT\n\n\u003c!-- prettier-ignore-start --\u003e\n[downloads-image]: https://img.shields.io/npm/dm/bach.svg?style=flat-square\n[npm-url]: https://www.npmjs.com/package/bach\n[npm-image]: https://img.shields.io/npm/v/bach.svg?style=flat-square\n\n[ci-url]: https://github.com/gulpjs/bach/actions?query=workflow:dev\n[ci-image]: https://img.shields.io/github/actions/workflow/status/gulpjs/bach/dev.yml?branch=master\u0026style=flat-square\n\n[coveralls-url]: https://coveralls.io/r/gulpjs/bach\n[coveralls-image]: https://img.shields.io/coveralls/gulpjs/bach.svg?style=flat-square\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- prettier-ignore-start --\u003e\n[domain]: https://nodejs.org/api/domain.html\n[async-done]: https://github.com/gulpjs/async-done\n[completions]: https://github.com/gulpjs/async-done#completion-and-error-resolution\n\u003c!-- prettier-ignore-end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgulpjs%2Fbach","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgulpjs%2Fbach","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgulpjs%2Fbach/lists"}