{"id":13548118,"url":"https://github.com/hgouveia/node-downloader-helper","last_synced_at":"2026-01-21T14:03:30.590Z","repository":{"id":40625765,"uuid":"132013872","full_name":"hgouveia/node-downloader-helper","owner":"hgouveia","description":"A simple http file downloader for node.js","archived":false,"fork":false,"pushed_at":"2025-12-04T08:01:21.000Z","size":186,"stargazers_count":260,"open_issues_count":4,"forks_count":54,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-12-06T06:37:55.246Z","etag":null,"topics":["downloader","http","nodejs"],"latest_commit_sha":null,"homepage":null,"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/hgouveia.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"ko_fi":"hgouveia","custom":["https://www.paypal.me/JoseDeGouveia","https://www.buymeacoffee.com/hgouveia"]}},"created_at":"2018-05-03T15:19:15.000Z","updated_at":"2025-12-04T07:58:26.000Z","dependencies_parsed_at":"2024-03-16T18:44:59.021Z","dependency_job_id":"e93e8187-ffe9-4194-8657-d74a6ce2836f","html_url":"https://github.com/hgouveia/node-downloader-helper","commit_stats":{"total_commits":87,"total_committers":18,"mean_commits":4.833333333333333,"dds":"0.29885057471264365","last_synced_commit":"bf7bd27ef460200d332473b833d601d2fe689b9b"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/hgouveia/node-downloader-helper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hgouveia%2Fnode-downloader-helper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hgouveia%2Fnode-downloader-helper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hgouveia%2Fnode-downloader-helper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hgouveia%2Fnode-downloader-helper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hgouveia","download_url":"https://codeload.github.com/hgouveia/node-downloader-helper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hgouveia%2Fnode-downloader-helper/sbom","scorecard":{"id":463604,"data":{"date":"2025-08-11","repo":{"name":"github.com/hgouveia/node-downloader-helper","commit":"e5cc3a5884fb4f90f86b2dae1b918450b6d24d42"},"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":"Code-Review","score":2,"reason":"Found 7/30 approved changesets -- score normalized to 2","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":"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/npmpublish.yml:1","Warn: no topLevel permission defined: .github/workflows/test.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":"Maintained","score":2,"reason":"0 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"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/npmpublish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/hgouveia/node-downloader-helper/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/hgouveia/node-downloader-helper/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/hgouveia/node-downloader-helper/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/hgouveia/node-downloader-helper/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/hgouveia/node-downloader-helper/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/hgouveia/node-downloader-helper/test.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/npmpublish.yml:19","Warn: npmCommand not pinned by hash: .github/workflows/npmpublish.yml:31","Warn: npmCommand not pinned by hash: .github/workflows/test.yml:22","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 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":"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":"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 7 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-19T11:51:17.092Z","repository_id":40625765,"created_at":"2025-08-19T11:51:17.092Z","updated_at":"2025-08-19T11:51:17.092Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28634786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T04:47:28.174Z","status":"ssl_error","status_checked_at":"2026-01-21T04:47:22.943Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["downloader","http","nodejs"],"created_at":"2024-08-01T12:01:05.968Z","updated_at":"2026-01-21T14:03:30.572Z","avatar_url":"https://github.com/hgouveia.png","language":"JavaScript","funding_links":["https://ko-fi.com/hgouveia","https://www.paypal.me/JoseDeGouveia","https://www.buymeacoffee.com/hgouveia"],"categories":["JavaScript"],"sub_categories":[],"readme":"# node-downloader-helper\n\n[![NPM Version](https://img.shields.io/npm/v/node-downloader-helper.svg?style=flat-square \"npm version\")](https://www.npmjs.com/package/node-downloader-helper)\n![npm](https://img.shields.io/npm/dw/node-downloader-helper?style=flat-square \"npm download\")\n![GitHub Actions Build](https://github.com/hgouveia/node-downloader-helper/actions/workflows/test.yml/badge.svg \"GitHub Actions Build\")\n[![Windows Build Status](https://img.shields.io/appveyor/ci/hgouveia/node-downloader-helper/master.svg?label=windows\u0026style=flat-square \"Windows Build Status\")](https://ci.appveyor.com/project/hgouveia/node-downloader-helper) [![Join the chat at https://gitter.im/node-downloader-helper/Lobby](https://badges.gitter.im/node-downloader-helper/Lobby.svg)](https://gitter.im/node-downloader-helper/Lobby?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fhgouveia%2Fnode-downloader-helper.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fhgouveia%2Fnode-downloader-helper?ref=badge_shield)\n\n\nA simple http file downloader for node.js\n\nFeatures:\n- No thirdparty dependecies\n- Pause/Resume\n- Retry on fail\n- Supports http/https\n- Supports http redirects\n- Supports pipes\n- Custom native http request options\n- Usable on vanilla nodejs, electron, nwjs\n- Progress stats\n\n## Install\n\n```\n$ npm install --save node-downloader-helper\n```\n\n## Usage\n\nFor a more complete example check [example](example/) folder\n\n```javascript\nconst { DownloaderHelper } = require('node-downloader-helper');\nconst dl = new DownloaderHelper('https://proof.ovh.net/files/1Gb.dat', __dirname);\n\ndl.on('end', () =\u003e console.log('Download Completed'));\ndl.on('error', (err) =\u003e console.log('Download Failed', err));\ndl.start().catch(err =\u003e console.error(err));\n```\n\n**IMPORTANT NOTE:** I highly recommend to use both `.on('error')` and `.start().catch`, although they do the same thing, if `on('error')` is not defined, an error will be thrown when the `error` event is emitted and not listing, this is because EventEmitter is designed to throw an `unhandled error event` error if not been listened and is too late to change it now.\n\n### CLI\n\nThis can be used as standalone CLI downloader\n\nInstall `npm i -g node-downloader-helper`\n\nUsage: `ndh [folder] [url]`\n\n```bash\n$ ndh ./folder http://url\n```\n\n## Options\n\nDownload Helper constructor also allow a 3rd parameter to set some options `constructor(url, destinationFolder, options)`,\nthese are the default values\n\n```javascript\n{\n    body: null, //  Request body, can be any, string, object, etc.\n    method: 'GET', // Request Method Verb\n    headers: {},  // Custom HTTP Header ex: Authorization, User-Agent\n    timeout: -1, // Request timeout in milliseconds (-1 use default), is the equivalent of 'httpRequestOptions: { timeout: value }' (also applied to https)\n    metadata: {}, // custom metadata for the user retrieve later (default:null)\n    resumeOnIncomplete: true, // Resume download if the file is incomplete (set false if using any pipe that modifies the file)\n    resumeOnIncompleteMaxRetry: 5, // Max retry when resumeOnIncomplete is true\n    resumeIfFileExists: false, // it will resume if a file already exists and is not completed, you might want to set removeOnStop and removeOnFail to false. If you used pipe for compression it will produce corrupted files\n    fileName: string|cb(fileName, filePath, contentType)|{name, ext}, // Custom filename when saved\n    retry: false, // { maxRetries: number, delay: number in ms } or false to disable (default)\n    forceResume: false, // If the server does not return the \"accept-ranges\" header, can be force if it does support it\n    removeOnStop: true, // remove the file when is stopped (default:true)\n    removeOnFail: true, // remove the file when fail (default:true)\n    progressThrottle: 1000, // interval time of the 'progress.throttled' event will be emitted\n    override: boolean|{skip, skipSmaller}, // Behavior when local file already exists\n    httpRequestOptions: {}, // Override the http request options  \n    httpsRequestOptions: {}, // Override the https request options, ex: to add SSL Certs\n}\n```\nfor `body` you can provide any parameter accepted by http.request write function `req.write(body)` https://nodejs.org/api/http.html, when using this, you might need to add the `content-length` and `content-type` header in addition with the http method `POST` or `PUT`\n\nex: \n```javascript\nconst data = JSON.stringify({\n  todo: 'Buy the milk'\n});\nconst dl = new DownloaderHelper('my_url', __dirname, { \nmethod: 'POST',\nbody: data,\nheaders: {\n    'Content-Type': 'application/json',\n    'Content-Length': data.length\n} } );\n```\n\nfor `fileName` you can provide 3 types of parameter\n - **string**: will use the full string as the filename including extension\n - **callback(fileName, filePath, contentType)**: must return an string, only sync function are supported ex: `(fileName) =\u003e 'PREFIX_' + fileName;`, **contentType** will be provided if available\n - **object**: this object must contain a `name` attribute and an optional `ext` attribute, the `ext` attribute can be an string without dot(`.`) or a boolean where `true` use the `name` as full file name (same as just giving an string to the `fileName` parameter) or false *(default)* will only replace the name and keep the original extension, for example if the original name is `myfile.zip` and the option is `{name: 'somename'}` the output will be `somename.zip`\n\nfor `override` you can provide 2 types of parameter\n- **boolean**: `true` to override existing local file, `false` to append '(number)' to new file name\n- **object**: object with properties `skip` (boolean): whether to skip download if file exists, and `skipSmaller` (boolean): whether to skip download if file exists but is smaller. Both default to `false`, for the equivalent of `override: true`.\n\nfor `httpRequestOptions` the available options are detailed in here https://nodejs.org/api/http.html#http_http_request_options_callback\n\nfor `httpsRequestOptions` the available options are detailed in here https://nodejs.org/api/https.html#https_https_request_options_callback\n\n\n## Methods\n\n| Name     \t| Description                                                                 \t|\n|----------\t|---------------------------------------------------------------------------\t|\n| start  \t| starts the downloading                                                       \t|\n| pause  \t| pause the downloading                                                        \t|\n| resume \t| resume the downloading if supported, if not it will start from the beginning \t|\n| stop   \t| stop the downloading and remove the file                                     \t|\n| pipe   \t| `readable.pipe(stream.Readable, options) : stream.Readable`                 \t|\n| unpipe   \t| `(stream)`  if not stream is not specified, then all pipes are detached.      |\n| updateOptions   \t| `(options, url)` updates the options, can be use on pause/resume events    |\n| getStats  | returns `stats` from the current download, these are the same `stats` sent via progress event  |\n| getTotalSize \t| gets the total file size from the server                                  |\n| getDownloadPath   | gets the full path where the file will be downloaded (available after the start phase) |\n| isResumable   \t| return true/false if the download can be resumable (available after the start phase) |\n| getResumeState   \t| Get the state required to resume the download after restart. This state can be passed back to `resumeFromFile()` to resume a download |\n| resumeFromFile   \t| `resumeFromFile(filePath?: string, state?: IResumeState)` Resume the download from a previous file path, if the state is not provided it will try to fetch from the information the headers and filePath, @see `resumeIfFileExists` option |\n\nusage of `resumeFromFile`\n\n```javascript\nconst downloadDir = 'D:/TEMP';\nconst { DownloaderHelper } = require('node-downloader-helper');\nconst dl = new DownloaderHelper('https://proof.ovh.net/files/1Gb.dat', downloadDir);\ndl.on('end', () =\u003e console.log('Download Completed'));\ndl.on('error', (err) =\u003e console.log('Download Failed', err));\n\n// option 1\nconst prevFilePath = `${downloadDir}/1Gb.dat`;\ndl.resumeFromFile(prevFilePath).catch(err =\u003e console.error(err));\n\n// option 2\nconst prevState = dl.getResumeState(); // this should be stored in a file, localStorage, db, etc in a previous process for example on 'stop'\ndl.resumeFromFile(prevState.filePath, prevState).catch(err =\u003e console.error(err));\n```\n\n## Events\n\n| Name        \t| Description                                                     \t                    |\n|--------------\t|-----------------------------------------------------------------------------------\t|\n| start        \t| Emitted when the .start method is called                      \t                    |\n| skip        \t| Emitted when the download is skipped because the file already exists                  |\n| download     \t| Emitted when the download starts `callback(downloadInfo)`        \t                    |\n| progress     \t| Emitted every time gets data from the server `callback(stats)` \t                    |\n| progress.throttled| The same as `progress` but emits every 1 second while is downloading `callback(stats)` |\n| retry        \t| Emitted when the download fails and retry is enabled `callback(attempt, retryOpts, err)`   |\n| end          \t| Emitted when the downloading has finished `callback(downloadInfo)`                    |\n| error        \t| Emitted when there is any error `callback(error)`              \t                    |\n| timeout      \t| Emitted when the underlying socket times out from inactivity.                         |\n| pause        \t| Emitted when the .pause method is called                      \t                    |\n| stop         \t| Emitted when the .stop method is called                       \t                    |\n| resume       \t| Emitted when the .resume method is called `callback(isResume)`   \t                    |\n| renamed      \t| Emitted when '(number)' is appended to the end of file, this requires `override:false` opt, `callback(filePaths)` |\n| redirected   \t| Emitted when an url redirect happened `callback(newUrl, oldUrl)` NOTE: this will be triggered during getTotalSize() as well |\n| stateChanged \t| Emitted when the state changes `callback(state)`               \t                    |\n| warning   \t| Emitted when an error occurs that was not thrown intentionally `callback(err: Error)` |\n\nevent **skip** `skipInfo` object\n```javascript\n{\n    totalSize:, // total file size got from the server (will be set as 'null' if content-length header is not available)\n    fileName:, // original file name\n    filePath:, // original path name\n    downloadedSize:, // the downloaded amount\n}\n```\n\nevent **download** `downloadInfo` object\n```javascript\n{\n    totalSize:, // total file size got from the server (will be set as 'null' if content-length header is not available)\n    fileName:, // assigned name\n    filePath:, // download path\n    isResumed:, // if the download is a resume,\n    downloadedSize:, // the downloaded amount (only if is resumed otherwise always 0)\n}\n```\n\nevent **progress** or **progress.throttled** `stats` object\n```javascript\n{\n    name:, // file name\n    total:, // total size that needs to be downloaded in bytes, (will be set as 'null' if content-length header is not available)\n    downloaded:, // downloaded size in bytes\n    progress:, // progress porcentage 0-100%, (will be set as 0 if total is null)\n    speed: // download speed in bytes\n}\n```\n\nevent **end** `downloadInfo` object\n```javascript\n{\n    fileName:, \n    filePath:,\n    totalSize:, // total file size got from the server, (will be set as 'null' if content-length header is not available)\n    incomplete:, // true/false if the download endend but still incomplete, set as 'false' if totalSize is null\n    onDiskSize, // total size of file on the disk\n    downloadedSize:, // the total size downloaded\n}\n```\n\nevent **renamed** `filePaths` object\n```javascript\n{\n    path:, // modified path name\n    fileName:, // modified file name\n    prevPath:, // original path name\n    prevFileName:, // original file name\n}\n```\n\nevent **error** `error` object\n```javascript\n{\n    message:, // Error message\n    status:, // Http status response if available\n    body:, // Http body response if available\n}\n```\n\n## States\n\n| Name         \t| Value                            \t|\n|--------------\t|----------------------------------\t|\n| IDLE         \t| 'IDLE'                           \t|\n| SKIPPED       | 'SKIPPED'                         |\n| STARTED      \t| 'STARTED'                        \t|\n| DOWNLOADING  \t| 'DOWNLOADING'                    \t|\n| PAUSED       \t| 'PAUSED'                         \t|\n| RESUMED      \t| 'RESUMED'                        \t|\n| STOPPED      \t| 'STOPPED'                        \t|\n| FINISHED     \t| 'FINISHED'                       \t|\n| FAILED       \t| 'FAILED'                         \t|\n| RETRY      \t| 'RETRY'                         \t|\n\n## Test\n\n```\n$ npm test\n```\n\n## License\n\nRead [License](LICENSE) for more licensing information.\n\n\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fhgouveia%2Fnode-downloader-helper.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fhgouveia%2Fnode-downloader-helper?ref=badge_large)\n\n## Contributing\n\nRead [here](CONTRIBUTING.md) for more information.\n\n## TODO\n- Better code testing\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhgouveia%2Fnode-downloader-helper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhgouveia%2Fnode-downloader-helper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhgouveia%2Fnode-downloader-helper/lists"}