@mantiq/events 0.5.20 → 0.5.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/Dispatcher.ts +25 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantiq/events",
3
- "version": "0.5.20",
3
+ "version": "0.5.22",
4
4
  "description": "Event dispatcher, listeners, observers, and broadcasting for MantiqJS",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/Dispatcher.ts CHANGED
@@ -38,8 +38,11 @@ export class Dispatcher implements EventDispatcher {
38
38
  */
39
39
  once(eventClass: Constructor<Event>, handler: EventHandler): void {
40
40
  const wrapper: EventHandler = async (event) => {
41
- this.off(eventClass, wrapper)
42
- await handler(event)
41
+ try {
42
+ await handler(event)
43
+ } finally {
44
+ this.off(eventClass, wrapper)
45
+ }
43
46
  }
44
47
  this.on(eventClass, wrapper)
45
48
  }
@@ -104,18 +107,32 @@ export class Dispatcher implements EventDispatcher {
104
107
 
105
108
  async emit(event: Event): Promise<void> {
106
109
  const eventClass = event.constructor as Constructor<Event>
107
- const registered = this.listeners.get(eventClass) ?? []
110
+ // Snapshot the array to avoid issues if listeners modify it during iteration
111
+ const registered = [...(this.listeners.get(eventClass) ?? [])]
112
+ const errors: ListenerError[] = []
108
113
 
109
- // Fire class-based and closure listeners
114
+ // Fire class-based and closure listeners — isolated, one failure doesn't stop others
110
115
  for (const listener of registered) {
111
- await this.callListener(listener, event)
116
+ try {
117
+ await this.callListener(listener, event)
118
+ } catch (error) {
119
+ if (error instanceof ListenerError) errors.push(error)
120
+ else errors.push(new ListenerError(event.constructor.name, 'unknown', error instanceof Error ? error : new Error(String(error))))
121
+ }
112
122
  }
113
123
 
114
- // Fire wildcard listeners
115
- for (const handler of this.wildcardListeners) {
116
- await handler(event)
124
+ // Fire wildcard listeners — also isolated
125
+ for (const handler of [...this.wildcardListeners]) {
126
+ try {
127
+ await handler(event)
128
+ } catch (error) {
129
+ errors.push(new ListenerError(event.constructor.name, handler.name || '(wildcard)', error instanceof Error ? error : new Error(String(error))))
130
+ }
117
131
  }
118
132
 
133
+ // Re-throw first error after all listeners have run (preserves the contract)
134
+ if (errors.length > 0) throw errors[0]
135
+
119
136
  // Broadcast if the event implements ShouldBroadcast
120
137
  if (this.broadcaster && isBroadcastable(event)) {
121
138
  await this.broadcaster.broadcast(event as Event & ShouldBroadcast)