{"id":20613985,"url":"https://github.com/jakelazaroff/cloth","last_synced_at":"2026-04-01T20:43:38.541Z","repository":{"id":57151990,"uuid":"79196196","full_name":"jakelazaroff/cloth","owner":"jakelazaroff","description":"A simple thread pool and task queue for Node.","archived":false,"fork":false,"pushed_at":"2017-09-25T23:06:06.000Z","size":30,"stargazers_count":3,"open_issues_count":1,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-19T01:41:02.702Z","etag":null,"topics":["childprocess","concurrent","multithreading","node","nodejs","parallel","pool","queue","task","task-queue","thread","thread-pool","worker-processes"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/cloth","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/jakelazaroff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-17T06:28:02.000Z","updated_at":"2017-12-10T07:24:33.000Z","dependencies_parsed_at":"2022-09-05T16:20:24.891Z","dependency_job_id":null,"html_url":"https://github.com/jakelazaroff/cloth","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jakelazaroff/cloth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakelazaroff%2Fcloth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakelazaroff%2Fcloth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakelazaroff%2Fcloth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakelazaroff%2Fcloth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakelazaroff","download_url":"https://codeload.github.com/jakelazaroff/cloth/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakelazaroff%2Fcloth/sbom","scorecard":{"id":502586,"data":{"date":"2025-08-11","repo":{"name":"github.com/jakelazaroff/cloth","commit":"7ff3121bbd98309318512f5b96b8c38e0ce93dc5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"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":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","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":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"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":"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":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"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":"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":"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":"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"}}]},"last_synced_at":"2025-08-19T22:21:00.936Z","repository_id":57151990,"created_at":"2025-08-19T22:21:00.936Z","updated_at":"2025-08-19T22:21:00.936Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"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":["childprocess","concurrent","multithreading","node","nodejs","parallel","pool","queue","task","task-queue","thread","thread-pool","worker-processes"],"created_at":"2024-11-16T11:11:35.216Z","updated_at":"2026-04-01T20:43:38.520Z","avatar_url":"https://github.com/jakelazaroff.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm](https://img.shields.io/npm/v/cloth.svg)](https://www.npmjs.com/package/cloth) [![Travis](https://img.shields.io/travis/jakelazaroff/cloth.svg)](https://travis-ci.org/jakelazaroff/cloth)\n\n# Cloth\n\nCloth is a simple thread pool and task queue for Node. It's a lightweight abstraction of Node's `child_process` and `EventEmitter` modules.\n\n```javascript\n// index.js\n\nconst Pool = require('cloth/pool');\n\nconst pool = new Pool(`${__dirname}/worker`);\n\npool.run('Hello, world!').on('end', message =\u003e {\n  console.log(message);\n});\n```\n\n```javascript\n// worker.js\n\nconst worker = require('cloth/worker');\n\nworker.run(command =\u003e {\n  console.log(command);\n  return 'Goodbye, world!';\n});\n```\n\n## Getting Started\n\n### Installation:\n\n```bash\nnpm install cloth --save\n```\n\n### Usage:\n\nInstantiate a pool with the path to the worker file:\n\n```javascript\nconst Pool = require('cloth/pool');\n\nconst pool = new Pool(`${__dirname}/worker`);\n```\n\nRun\n\n```javascript\nconst task = pool.run('Hello, world!');\n```\n\nThe argument we're passing is the task's command. Here, the command is a string, but it can be an array, object or any other serializable data structure.\n\nSince this is the first task we're running, all the workers are idle and the task will be run immediately. If we try to run tasks faster than our workers finish them, tasks will be put into a queue where they'll run as soon as a worker finishes its current task.\n\nWhat if we want information back from a task? We can listen to events it sends back:\n\n```javascript\ntask.on('end', message =\u003e {\n  console.log(message);\n});\n```\n\nThe `end` event occurs when the worker finishes processing the task. But what happens if an error prevents it from finishing?\n\n```javascript\ntask\n  .on('end', message =\u003e {\n    console.log(message);\n  })\n  .on('error', err =\u003e {\n    console.log(err);\n  });\n```\n\nWe can chain together as many `on` calls as we'd like this way — and other than `start`, `end` and `error`, we can have our worker send back custom events as well:\n\n```javascript\ntask\n  .on('end', message =\u003e {\n    console.log(message);\n  })\n  .on('error', err =\u003e {\n    console.log(err);\n  })\n  .on('test', err =\u003e {\n    console.log(err);\n  });\n```\n\nAll of which brings us to:\n\n```javascript\n// worker.js\n\nconst worker = require('cloth/worker');\n\nworker.run(command =\u003e {\n  console.log(command);\n  return 'Goodbye, world!';\n});\n```\n\nRemember how we instantiated the pool with the path to the worker file? This is that file!\n\nThe most important worker method is `run`. Every time the worker picks up a task, the callback supplied to this method is invoked with the task's command. We process the command however we want and return the result, and the worker will take care of sending the results back to the main process:\n\n```javascript\nworker.run(command =\u003e {\n  console.log(command);\n  return 'Goodbye, world!';\n});\n```\n\nIf we run into problems, we just have to throw an error and the worker will let the main process know:\n\n```javascript\nworker.run(command =\u003e {\n  if (!command) {\n    throw new Error('No command!');\n  }\n  console.log(command);\n  return 'Goodbye, world!';\n});\n```\n\nIf our task is asynchronous, we can add a callback parameter and deal with things in traditional Node style:\n\n```javascript\nworker.run((command, callback) =\u003e {\n  if (!command) {\n    callback(new Error('No command!'));\n  }\n  console.log(command);\n  callback(null, 'Goodbye, world!');\n});\n```\n\nFinally, if we need to, we can also send intermediate results back to the main process:\n\n```javascript\nworker.run((command, callback) =\u003e {\n  if (!command) {\n    callback(new Error('No command!'));\n  }\n  worker.send('test', 'message');\n  console.log(command);\n  callback(null, 'Goodbye, world!');\n});\n```\n\nThat's the quick intro to Cloth! Check out the API docs below for more information. Happy parallelism!\n\n##API\n\n### Pool\n\nA Pool is the main process's interface to the child processes. It manages the worker pool and task queue to optimize concurrency.\n\n#### constructor(worker, [options])\n\nCreates a new pool with a given worker file. By default, it will create the same number of workers as CPU cores (`require('os').cpus().length`) but this can be overridden in the options.\n\n**options**\n\n- `workers`: the number of workers to create\n- `arguments`: an array of string arguments to be sent to each worker as it's created\n\n#### run(command)\n\nCreates a task with the given command to be run as soon as a worker is available. Returns the task.\n\n#### on(event, listener)\n\nCalls the supplied listener with the event message and task whenever an event of the specified type is triggered by any task.\n\n#### total()\n\nReturns the number of workers in the pool.\n\n#### available()\n\nReturns the number of workers in the pool that are not currently processing tasks.\n\n#### drain()\n\nKills all the worker processes and removes all the tasks from the queue.\n\n### worker\n\nA worker is the children processes' interface to the main process. It invokes a user-defined function to process tasks and manages task lifecycle events and error handling.\n\n#### run(fn)\n\nInvokes the supplied function with the task's command whenever the worker picks up a task. The function signature should be `fn(command, [callback])`. If it doesn't take a callback, returning any value will cause the task to end successfully; if it **does** take a callback, errors can be sent with `callback(err)` and successful results with `callback(null, results)`. Throwing an error will also make the worker send an error back to the main process.\n\n#### send(event, message)\n\nSends an event with the supplied type and message back to the main process. Any listeners for that event type on either the task or the pool will be triggered.\n\n#### arguments\n\nThe arguments passed to the Pool when it was instantiated in the main process.\n\n### Task\n\nA set of instructions that are run as soon as a worker frees up. Tasks are picked up in the order they're sent.\n\n#### on(event, listener)\n\nCalls the supplied listener with the event message and task whenever an event of the specified type is triggered by this specific task.\n\n#### state\n\nThe state of a task with regard to the queue; one of four possibilities:\n\n- `queued`: the task hasn't yet been picked up\n- `processing`: the task is currently running\n- `finished`: the task completed successfully\n- `error`: the task couldn't complete because of an error\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakelazaroff%2Fcloth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakelazaroff%2Fcloth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakelazaroff%2Fcloth/lists"}