{"_id":"@bupkis/events","_rev":"3-b7b55601af70cb7dc15a15cc9ca29d92","name":"@bupkis/events","dist-tags":{"latest":"1.0.0"},"versions":{"0.1.1":{"name":"@bupkis/events","version":"0.1.1","keywords":["bupkis","events","eventemitter","eventtarget","assert","assertion","test"],"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","_id":"@bupkis/events@0.1.1","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"homepage":"https://github.com/boneskull/bupkis#readme","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"dist":{"shasum":"dc8995395bc50494d6a9469a02d3c38f192056ef","tarball":"https://registry.npmjs.org/@bupkis/events/-/events-0.1.1.tgz","fileCount":49,"integrity":"sha512-LkKNoGF4PnAZ6jkAphzPMDBbmwwWv93O7LdFpo+ii88MBWhcNStG39kz25WQ0iHFFDEuVPg0nHJXCsyZoDbeHg==","signatures":[{"sig":"MEYCIQCspj0vr5OdzRYSq6vVRRY+TtLHdA0pEVLxKyEPeQyAKwIhAJ+3OOoqz28Jg0R5P/b15J1vDCCNLrXllp7+odHg04rF","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206585},"main":"./dist/index.cjs","type":"module","zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"types":"./dist/index.d.cts","module":"./dist/index.js","engines":{"node":"^20.19.0 || ^22.12.0 || >=23"},"exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"ca2eb98497de0200e5861cb8b7de9d48ca26439d","scripts":{"test":"npm run test:base -- \"test/*.test.ts\"","build":"zshy","test:dev":"npm run test:base -- --watch \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:types":"tsd","test:node20":"npm run test:base -- test/*.test.ts","prepublishOnly":"npm run build"},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"prettier":{"tsdoc":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg","prettier-plugin-sort-json"],"singleQuote":true,"jsdocPreferCodeFences":true,"jsdocCommentLineStrategy":"keep"},"repository":{"url":"git+https://github.com/boneskull/bupkis.git","type":"git","directory":"packages/events"},"_npmVersion":"11.6.2","description":"EventEmitter and EventTarget assertions for Bupkis","directories":{},"_nodeVersion":"24.12.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"fast-check":"^4.5.2","eventemitter3":"5.0.1"},"peerDependencies":{"bupkis":">=0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/events_0.1.1_1768432858139_0.5274692764571645","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bupkis/events","version":"0.2.0","keywords":["bupkis","events","eventemitter","eventtarget","assert","assertion","test"],"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","_id":"@bupkis/events@0.2.0","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"homepage":"https://github.com/boneskull/bupkis#readme","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"dist":{"shasum":"a9734d5059007bd36ccc14966d7bce45ef4569b8","tarball":"https://registry.npmjs.org/@bupkis/events/-/events-0.2.0.tgz","fileCount":49,"integrity":"sha512-6mExHz6INcZ44MiIYp/qh65n54z2L8PrSw38hbiwQ1J2BUGhc4ya1gjCJ79s6OvHdmTImKyvdo2glX3kGXZyng==","signatures":[{"sig":"MEUCIQDo6p9Ko11Jtxmq5CuMWwaB5yqOVQ5rBlfS/DOshoMGggIgH/M8yg1YZ963onqKa31qAVt0L6+qwDkga4LU953hGdI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206896},"main":"./dist/index.cjs","type":"module","zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"types":"./dist/index.d.cts","module":"./dist/index.js","engines":{"node":"^20.19.0 || ^22.12.0 || >=23"},"exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"f09ff1cfb21ceb03dee850971611a3d1e98086b4","scripts":{"test":"npm run test:base -- \"test/*.test.ts\"","build":"zshy","test:dev":"npm run test:base -- --watch \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:types":"tsd","test:node20":"npm run test:base -- test/*.test.ts","prepublishOnly":"npm run build"},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"prettier":{"tsdoc":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg"],"singleQuote":true,"jsdocPreferCodeFences":true,"jsdocCommentLineStrategy":"keep"},"repository":{"url":"git+https://github.com/boneskull/bupkis.git","type":"git","directory":"packages/events"},"_npmVersion":"11.6.2","description":"EventEmitter and EventTarget assertions for Bupkis","directories":{},"_nodeVersion":"24.12.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"fast-check":"^4.5.2","eventemitter3":"5.0.1"},"peerDependencies":{"bupkis":">=0.15.0"},"_npmOperationalInternal":{"tmp":"tmp/events_0.2.0_1768604253586_0.2533365194143451","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@bupkis/events","version":"1.0.0","type":"module","description":"EventEmitter and EventTarget assertions for Bupkis","repository":{"directory":"packages/events","type":"git","url":"git+https://github.com/boneskull/bupkis.git"},"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","engines":{"node":"^22.13.0 || >=24.0.0"},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.cts","import":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"keywords":["bupkis","events","eventemitter","eventtarget","assert","assertion","test"],"scripts":{"build":"zshy","prepublishOnly":"npm run build","test":"npm run test:base -- \"test/*.test.ts\"","test:base":"node --import tsx --test --test-reporter=spec","test:ci":"npm run test -- --test-reporter=node-test-github-reporter","test:coverage":"c8 --reporter=lcov --reporter=text npm test","test:dev":"npm run test -- --watch"},"peerDependencies":{"bupkis":">=0.15.0"},"devDependencies":{"eventemitter3":"5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"prettier":{"jsdocCommentLineStrategy":"keep","jsdocPreferCodeFences":true,"plugins":["prettier-plugin-jsdoc","prettier-plugin-pkg"],"singleQuote":true,"tsdoc":true},"zshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"}},"gitHead":"c0f3fcd74ca0f7e307d2a68ce12bba81b4059eb5","_id":"@bupkis/events@1.0.0","bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"homepage":"https://github.com/boneskull/bupkis#readme","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-9e30AQGuL6bVQACvSDq3YdcPbQXCMkRuTprE58imkdRiZWlG+0U2dFLJKUvFvx9PP1HQki832SRF/zEk7TTzoQ==","shasum":"201dbc93b9d53ee7bab5693b7a83090612da44dd","tarball":"https://registry.npmjs.org/@bupkis/events/-/events-1.0.0.tgz","fileCount":49,"unpackedSize":207483,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDYtKrOx793rpp3Lz71UQpmN7QyzBz2Z7iV8vmcYoo3tAiEA+JCn4wvR66JkphT1bgocan1lxals9imwbSPN3wkza3g="}]},"_npmUser":{"name":"boneskull","email":"boneskull@boneskull.com"},"directories":{},"maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/events_1.0.0_1783650175849_0.44637966453615263"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-14T23:20:57.957Z","modified":"2026-07-10T02:22:56.140Z","0.1.1":"2026-01-14T23:20:58.294Z","0.2.0":"2026-01-16T22:57:33.759Z","1.0.0":"2026-07-10T02:22:56.003Z"},"bugs":{"url":"https://github.com/boneskull/bupkis/issues"},"author":{"name":"Christopher Hiller","email":"boneskull@boneskull.com"},"license":"BlueOak-1.0.0","homepage":"https://github.com/boneskull/bupkis#readme","keywords":["bupkis","events","eventemitter","eventtarget","assert","assertion","test"],"repository":{"directory":"packages/events","type":"git","url":"git+https://github.com/boneskull/bupkis.git"},"description":"EventEmitter and EventTarget assertions for Bupkis","maintainers":[{"name":"boneskull","email":"boneskull@boneskull.com"}],"readme":"# @bupkis/events\n\nEventEmitter and EventTarget assertions for [bupkis](https://github.com/boneskull/bupkis).\n\n## Installation\n\n```bash\nnpm install @bupkis/events\n```\n\n## Usage\n\n```ts\nimport { use } from 'bupkis';\nimport { eventAssertions } from '@bupkis/events';\nimport { EventEmitter } from 'node:events';\n\n// Register the assertions\nconst { expect, expectAsync } = use(eventAssertions);\n\n// Sync assertions for listener state\nconst emitter = new EventEmitter();\nemitter.on('data', () => {});\nexpect(emitter, 'to have listener for', 'data');\n\n// Async assertions for event emission\nawait expectAsync(\n  () => emitter.emit('ready'),\n  'to emit from',\n  emitter,\n  'ready',\n);\n```\n\n## Assertions\n\n### Synchronous Assertions (EventEmitter)\n\n#### to have listener for {event}\n\nAsserts that an emitter has at least one listener for the specified event.\n\n```ts\nconst emitter = new EventEmitter();\nemitter.on('data', () => {});\n\nexpect(emitter, 'to have listener for', 'data'); // passes\nexpect(emitter, 'not to have listener for', 'other'); // passes (negation)\n```\n\n#### to have listeners for {events}\n\nAsserts that an emitter has listeners for all specified events.\n\n```ts\nconst emitter = new EventEmitter();\nemitter.on('data', () => {});\nemitter.on('end', () => {});\n\nexpect(emitter, 'to have listeners for', ['data', 'end']); // passes\n```\n\n#### to have listener count {event} {count}\n\nAsserts that an emitter has exactly the specified number of listeners for an event.\n\n```ts\nconst emitter = new EventEmitter();\nemitter.on('data', () => {});\nemitter.on('data', () => {}); // second listener\n\nexpect(emitter, 'to have listener count', 'data', 2); // passes\n```\n\n#### to have listeners\n\nAsserts that an emitter has at least one listener registered (for any event).\n\n```ts\nconst emitter = new EventEmitter();\nemitter.on('anything', () => {});\n\nexpect(emitter, 'to have listeners'); // passes\nexpect(new EventEmitter(), 'not to have listeners'); // passes (fresh emitter)\n```\n\n#### to have max listeners {count}\n\nAsserts that an emitter's maxListeners is set to the specified value.\n\n```ts\nconst emitter = new EventEmitter();\nemitter.setMaxListeners(20);\n\nexpect(emitter, 'to have max listeners', 20); // passes\n```\n\n### Asynchronous Assertions (EventEmitter)\n\nAsync assertions use a trigger-based API where the first argument is a function or Promise that causes the event to be emitted.\n\n#### to emit from {emitter} {event}\n\nAsserts that a trigger causes an emitter to emit the specified event.\n\n```ts\nconst emitter = new EventEmitter();\n\n// Function trigger\nawait expectAsync(\n  () => emitter.emit('ready'),\n  'to emit from',\n  emitter,\n  'ready',\n);\n\n// Async trigger\nawait expectAsync(\n  () => setTimeout(() => emitter.emit('ready'), 10),\n  'to emit from',\n  emitter,\n  'ready',\n);\n```\n\n#### to emit from {emitter} {event} with args {args}\n\nAsserts that a trigger causes an emitter to emit an event with specific arguments. Uses `'to satisfy'` semantics, allowing partial matching for objects and `expect.it()` for custom assertions.\n\n```ts\nconst emitter = new EventEmitter();\n\nawait expectAsync(\n  () => emitter.emit('data', 'hello', 42),\n  'to emit from',\n  emitter,\n  'data',\n  'with args',\n  ['hello', 42],\n);\n```\n\nWith custom assertions:\n\n```ts\nawait expectAsync(\n  () => emitter.emit('data', { count: 42, extra: 'ignored' }),\n  'to emit from',\n  emitter,\n  'data',\n  'with args',\n  [expect.it('to satisfy', { count: expect.it('to be greater than', 0) })],\n);\n```\n\n#### to emit error from {emitter}\n\nAsserts that a trigger causes an emitter to emit the 'error' event.\n\n```ts\nconst emitter = new EventEmitter();\nemitter.on('error', () => {}); // Prevent unhandled error\n\nawait expectAsync(\n  () => emitter.emit('error', new Error('oops')),\n  'to emit error from',\n  emitter,\n);\n```\n\n#### to emit events from {emitter} {events}\n\nAsserts that a trigger causes an emitter to emit events in the specified order.\n\n```ts\nconst emitter = new EventEmitter();\n\nawait expectAsync(\n  () => {\n    emitter.emit('start');\n    emitter.emit('data');\n    emitter.emit('end');\n  },\n  'to emit events from',\n  emitter,\n  ['start', 'data', 'end'],\n);\n```\n\n### Asynchronous Assertions (EventTarget)\n\n#### to dispatch from {target} {eventType}\n\nAsserts that a trigger causes an EventTarget to dispatch the specified event.\n\n```ts\nconst target = new EventTarget();\n\nawait expectAsync(\n  () => target.dispatchEvent(new Event('click')),\n  'to dispatch from',\n  target,\n  'click',\n);\n```\n\n#### to dispatch from {target} {eventType} with detail {detail}\n\nAsserts that a trigger causes an EventTarget to dispatch a CustomEvent with specific detail. Uses `'to satisfy'` semantics for objects, allowing partial matching and `expect.it()` for custom assertions.\n\n```ts\nconst target = new EventTarget();\n\nawait expectAsync(\n  () =>\n    target.dispatchEvent(new CustomEvent('custom', { detail: { foo: 'bar' } })),\n  'to dispatch from',\n  target,\n  'custom',\n  'with detail',\n  { foo: 'bar' },\n);\n```\n\nPartial matching (extra properties allowed):\n\n```ts\nawait expectAsync(\n  () =>\n    target.dispatchEvent(\n      new CustomEvent('custom', { detail: { foo: 'bar', extra: 'ignored' } }),\n    ),\n  'to dispatch from',\n  target,\n  'custom',\n  'with detail',\n  { foo: 'bar' },\n);\n```\n\nWith custom assertions:\n\n```ts\nawait expectAsync(\n  () =>\n    target.dispatchEvent(new CustomEvent('data', { detail: { count: 42 } })),\n  'to dispatch from',\n  target,\n  'data',\n  'with detail',\n  expect.it('to satisfy', { count: expect.it('to be greater than', 0) }),\n);\n```\n\n### Timeout Options\n\nAll async assertions accept an optional timeout configuration:\n\n```ts\n// Wait up to 100ms for the event\nawait expectAsync(\n  () => setTimeout(() => emitter.emit('slow'), 50),\n  'to emit from',\n  emitter,\n  'slow',\n  { within: 100 },\n);\n```\n\nThe default timeout is 2000ms.\n\n## Duck-Typed EventEmitter Support\n\nThis package uses duck-typing to detect EventEmitter-like objects, making it compatible with:\n\n- Node.js `EventEmitter`\n- `eventemitter3`\n- Custom implementations that match the interface\n\n```ts\nimport EventEmitter3 from 'eventemitter3';\n\nconst emitter = new EventEmitter3();\nemitter.on('data', () => {});\n\nexpect(emitter, 'to have listener for', 'data'); // works!\n```\n\n## Symbol Events\n\nBoth sync and async assertions support symbol event names:\n\n```ts\nconst sym = Symbol('myEvent');\nemitter.on(sym, () => {});\n\nexpect(emitter, 'to have listener for', sym);\n\nawait expectAsync(() => emitter.emit(sym), 'to emit from', emitter, sym);\n```\n\n## License\n\n[BlueOak-1.0.0](https://blueoakcouncil.org/license/1.0.0)\n","readmeFilename":"README.md"}