Skip to content

EventBus: emitting a wildcard literal double-fires handlers registered on the same literal name #1

@razukc

Description

@razukc

Summary

When a handler is registered on an event name that contains * (e.g. *, user.*), EventBus stores it in wildcardHandlers rather than handlers. Emitting that exact same literal name then triggers both:

  1. the wildcard match (because * matches any event, including itself), and
  2. the exact-match wildcard entry,

so each registered handler fires twice. The same problem applies to any pattern literal — registering on "foo.*" and emitting "foo.*" double-fires.

Reproduction

```ts
import { Runtime } from 'writmint';

const rt = new Runtime();
const calls: number[] = [];
rt.registerPlugin({
name: 'p', version: '1.0.0',
setup: async (ctx) => {
ctx.events.on('', () => calls.push(1));
ctx.events.on('
', () => calls.push(2));
},
});
await rt.initialize();
rt.getContext().events.emit('*');
console.log(calls.length); // 4 — expected 2
```

Where

  • `src/event-bus.ts` — `emit` walks both `handlers` and `wildcardHandlers` and a literal-pattern handler matches both paths for its own name.
  • See `tests/property/event-delivery.property.test.ts` (top-of-file comment) — the property suite excludes `*` from generated event names to work around this.

Suggested fix

In `emit`, when iterating wildcard handlers, skip entries whose pattern equals the emitted event name and was already delivered via the exact-match path; or, equivalently, route a literal pattern (`isWildcard(pattern) && pattern === eventName`) through one path only.

Acceptance

  • The reproduction above prints `2`.
  • Remove the `.filter((s) => !s.includes('*'))` workaround on the `eventNameArb` generator in `tests/property/event-delivery.property.test.ts` and the suite stays green across many seeds.

Found

While running the prepublish gate for v0.1.0; fast-check counterexample `[2, "*"]` on seed `-1796970649`.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions