{"id":17134699,"url":"https://github.com/robojones/better-events","last_synced_at":"2026-04-30T18:31:45.645Z","repository":{"id":47705048,"uuid":"75295983","full_name":"robojones/better-events","owner":"robojones","description":"Improved version of the Node.js EventEmitter","archived":false,"fork":false,"pushed_at":"2023-04-17T17:44:19.000Z","size":130,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-30T08:19:13.982Z","etag":null,"topics":["event-emitter","node-eventemitter","nodejs","promise"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/better-events","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/robojones.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-12-01T13:39:02.000Z","updated_at":"2021-08-17T11:46:44.000Z","dependencies_parsed_at":"2023-12-14T23:45:34.652Z","dependency_job_id":null,"html_url":"https://github.com/robojones/better-events","commit_stats":{"total_commits":78,"total_committers":3,"mean_commits":26.0,"dds":"0.14102564102564108","last_synced_commit":"572ad1cbe2124bd403edcd5a717f99b28b0181e7"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/robojones/better-events","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robojones%2Fbetter-events","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robojones%2Fbetter-events/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robojones%2Fbetter-events/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robojones%2Fbetter-events/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robojones","download_url":"https://codeload.github.com/robojones/better-events/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robojones%2Fbetter-events/sbom","scorecard":{"id":781061,"data":{"date":"2025-08-11","repo":{"name":"github.com/robojones/better-events","commit":"572ad1cbe2124bd403edcd5a717f99b28b0181e7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"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":"Code-Review","score":0,"reason":"Found 0/21 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":"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":"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":-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":"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":"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":"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 9 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"}},{"name":"Vulnerabilities","score":0,"reason":"14 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T04:56:07.935Z","repository_id":47705048,"created_at":"2025-08-23T04:56:07.935Z","updated_at":"2025-08-23T04:56:07.935Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32473804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"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":["event-emitter","node-eventemitter","nodejs","promise"],"created_at":"2024-10-14T19:45:31.773Z","updated_at":"2026-04-30T18:31:45.619Z","avatar_url":"https://github.com/robojones.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BetterEvents\nAn improved version of the Node.js EventEmitter.\n\n[![Build Status](https://travis-ci.org/robojones/better-events.svg?branch=master)](https://travis-ci.org/robojones/better-events)\n[![Test Coverage](https://codeclimate.com/github/robojones/better-events/badges/coverage.svg)](https://codeclimate.com/github/robojones/better-events/coverage)\n\n[![bitHound Code](https://www.bithound.io/github/robojones/better-events/badges/code.svg)](https://www.bithound.io/github/robojones/better-events)\n[![bitHound Overall Score](https://www.bithound.io/github/robojones/better-events/badges/score.svg)](https://www.bithound.io/github/robojones/better-events)\n[![bitHound Dependencies](https://www.bithound.io/github/robojones/better-events/badges/dependencies.svg)](https://www.bithound.io/github/robojones/better-events/master/dependencies/npm)\n[![bitHound Dev Dependencies](https://www.bithound.io/github/robojones/better-events/badges/devDependencies.svg)](https://www.bithound.io/github/robojones/better-events/master/dependencies/npm)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Installation\n\n```\nnpm i better-events --save\n```\n\n## Changes\n\n- v3.0.0\n  - The promises returned by the [BetterEvents.once](#bettereventsoncesource-eventname-arraymode) and [emitter.once](#emitteronceeventname-listener) methods now get cached. The same promise gets returned until the event gets emitted.\n  - Add static [shareEvent](#bettereventsshareeventeventname-source-target-once) method.\n- v2.0.0\n  - Promises for the \"error\" event will now be rejected when an error is emitted.\n\n## Class: BetterEvents\n\nThe BetterEvents constructor extends the default Node.js EventEmitter.\n\nFor more information on the EventEmitter see the Node.js docs:\nhttps://nodejs.org/api/events.html\n\nHere are the specs for the new methods of BetterEvents.\n\n### BetterEvents.once(source, eventName[, arrayMode])\n\n- __source__ `\u003cEventEmitter\u003e` The source for the Event.\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __arrayMode__ `\u003cBoolean\u003e` resolve the promise with an array containing all arguments of the event\n\n__Returns__ a `\u003cPromise\u003e` that gets resolved with the first argument of the event.\nIt gets resolved when the source emits the event.\nIf the eventName is \"error\" then the promise gets rejected as soon as an error is emitted.\n\n```javascript\nconst { once } = require('better-events')\n\nasync function read() {\n  const readstream = fs.createReadStream('example.txt')\n\n  let text = ''\n  readstream.on('data', chunk =\u003e {\n    text += chunk\n  })\n\n  // Await the \"close\" event.\n  await once(readstream, 'close')\n\n  // Log the contents of the file.\n  console.log('text:', text)\n}\n\nread()\n```\n\n### BetterEvents.shareEvent(eventName, source, target, once)\n\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __source__ `\u003cEventEmitter\u003e` the EventEmitter that emits the event.\n- __target__ `\u003cEventEmitter\u003e` The EventEmitter to share the event with.\n- __once__ `\u003cBoolean\u003e` Share the event only once.\n\n__Returns__ the listener that has been applied to the source so one can use _.removeListener()_.\n\nThis method allows you to share events. If the event gets emitted on the _source_, it also gets emitted on the _target_. This works only __one way__. If the shared event gets emitted on the _target_, it is __not__ emitted on the _source_.\n\n```javascript\nconst {\n  BetterEvents,\n  shareEvent\n} = require('better-events')\n\nconst emitter1 = new BetterEvents()\nconst emitter2 = new BetterEvents()\n\nshareEvent('hello', emitter1, emitter2)\n\nemitter2.on('hello', () =\u003e console.log('received event'))\n\nemitter1.emit('hello')\n// \"received event\" will be logged.\n```\n\n### emitter.once(eventName[, listener])\n\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __listener__ `\u003cFunction\u003e` | `\u003cBoolean\u003e` callback function\n\nLike the original method (see: https://nodejs.org/api/events.html#events_emitter_once_eventname_listener) with exceptions.\n\nIf no callback is provided the method returns a `\u003cPromise\u003e` that resolves with the first argument of the event when the event is fired.\nIf the eventName is \"error\" then the promise gets rejected as soon as an error is emitted.\n\nIf one provides `true` instead of the callback the array mode gets activated.\nThe method returns a `\u003cPromise\u003e` which resolves with an array containing all the arguments of the event.\nIt gets resolved when the event is fired.\n\n```javascript\nconst BetterEvents = require('better-events')\n\n// Create your own class that extends BetterEvents.\nclass Seconds extends BetterEvents {\n  constructor() {\n    setInterval(() =\u003e {\n      this.emit('second')\n    }, 1000)\n  }\n}\n\n// Create an instance of your class.\nconst example = new Seconds()\n\n// Get a promise for the 'second' event.\nexample.once('second').then(() =\u003e {\n  console.log('one second has passed')\n})\n```\n\n### emitter.collect(eventName, source)\n\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __source__ `\u003cEventEmitter\u003e` The source for the Event.\n\n__Returns__ the listener that has been applied to the source so one can use _.removeListener()_.\n\nWhen the event specified by the __eventName__ gets fired at the __source__ it will also be emitted on this instance.\n\n### emitter.collectOnce(eventName, source)\n\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __source__ `\u003cEventEmitter\u003e` The target for the Event.\n\n__Returns__ the listener that has been applied to the source so one can use _.removeListener()_.\n\nSimilar to _emitter.collect()_ but works only once.\n\n### emitter.share(eventName, target)\n\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __target__ `\u003cEventEmitter\u003e` The target for the Event.\n\n__Returns__ the listener that has been applied to the source so one can use _.removeListener()_.\n\nWhen the event specified by the __eventName__ gets fired at this instance it will also be emitted on the __target__.\n\n### emitter.shareOnce(eventName, target)\n\n- __eventName__ `\u003cString\u003e` | `\u003cSymbol\u003e` The name of the event.\n- __target__ `\u003cEventEmitter\u003e` The target for the Event.\n\n__Returns__ the listener that has been applied to the source so one can use _.removeListener()_.\n\nSimilar to _emitter.share()_ but works only once.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobojones%2Fbetter-events","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobojones%2Fbetter-events","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobojones%2Fbetter-events/lists"}