events-ex 2.3.0 → 2.4.0

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 (122) hide show
  1. package/.babelrc +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/README.cn.md +114 -3
  4. package/README.md +106 -5
  5. package/coverage/all-off.js.html +133 -0
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/clover.xml +572 -0
  9. package/coverage/consts.js.html +154 -0
  10. package/coverage/default-methods.js.html +2068 -0
  11. package/coverage/event-emitter.js.html +121 -0
  12. package/coverage/event.js.html +286 -0
  13. package/coverage/eventable.js.html +355 -0
  14. package/coverage/favicon.png +0 -0
  15. package/coverage/has-listeners.js.html +166 -0
  16. package/coverage/index.html +281 -0
  17. package/coverage/lcov-report/all-off.js.html +133 -0
  18. package/coverage/lcov-report/base.css +224 -0
  19. package/coverage/lcov-report/block-navigation.js +87 -0
  20. package/coverage/lcov-report/consts.js.html +154 -0
  21. package/coverage/lcov-report/default-methods.js.html +2068 -0
  22. package/coverage/lcov-report/event-emitter.js.html +121 -0
  23. package/coverage/lcov-report/event.js.html +286 -0
  24. package/coverage/lcov-report/eventable.js.html +355 -0
  25. package/coverage/lcov-report/favicon.png +0 -0
  26. package/coverage/lcov-report/has-listeners.js.html +166 -0
  27. package/coverage/lcov-report/index.html +281 -0
  28. package/coverage/lcov-report/once-promise.js.html +325 -0
  29. package/coverage/lcov-report/pipe-async.js.html +385 -0
  30. package/coverage/lcov-report/pipe.js.html +244 -0
  31. package/coverage/lcov-report/prettify.css +1 -0
  32. package/coverage/lcov-report/prettify.js +2 -0
  33. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  34. package/coverage/lcov-report/sorter.js +210 -0
  35. package/coverage/lcov-report/unify.js.html +259 -0
  36. package/coverage/lcov-report/wrap-event-emitter.js.html +223 -0
  37. package/coverage/lcov.info +1201 -0
  38. package/coverage/once-promise.js.html +325 -0
  39. package/coverage/pipe-async.js.html +385 -0
  40. package/coverage/pipe.js.html +244 -0
  41. package/coverage/prettify.css +1 -0
  42. package/coverage/prettify.js +2 -0
  43. package/coverage/sort-arrow-sprite.png +0 -0
  44. package/coverage/sorter.js +210 -0
  45. package/coverage/unify.js.html +259 -0
  46. package/coverage/wrap-event-emitter.js.html +223 -0
  47. package/docs/README.md +106 -5
  48. package/docs/all-off/functions/allOff.md +1 -1
  49. package/docs/consts/README.md +4 -0
  50. package/docs/consts/functions/createAbortError.md +18 -0
  51. package/docs/consts/variables/RegExpEventSymbol.md +1 -1
  52. package/docs/consts/variables/states.md +2 -2
  53. package/docs/default-methods/functions/getEventableMethods.md +34 -18
  54. package/docs/event/classes/Event.md +23 -13
  55. package/docs/event-emitter/classes/EventEmitter-1.md +2 -2
  56. package/docs/event-emitter/classes/EventEmitter.md +28 -28
  57. package/docs/eventable/functions/eventable.md +1 -1
  58. package/docs/has-listeners/functions/hasListeners.md +2 -2
  59. package/docs/index/README.md +12 -0
  60. package/docs/modules.md +1 -0
  61. package/docs/once-promise/README.md +17 -0
  62. package/docs/once-promise/functions/oncePromise.md +60 -0
  63. package/docs/pipe/functions/pipe.md +3 -3
  64. package/docs/pipe-async/functions/pipeAsync.md +3 -3
  65. package/docs/unify/functions/unify.md +1 -1
  66. package/docs/util/array-remove/functions/remove.md +1 -1
  67. package/docs/util/object-for-each/functions/forEach.md +1 -1
  68. package/docs/util/string-pad/functions/pad.md +1 -1
  69. package/docs/util/to-int/functions/toInt.md +1 -1
  70. package/docs/util/valid-callable/functions/validCallable.md +1 -1
  71. package/docs/util/valid-object/functions/validObject.md +1 -1
  72. package/docs/wrap-event-emitter/functions/wrapEventEmitter.md +3 -3
  73. package/docs/wrap-event-emitter/variables/methods.md +1 -1
  74. package/lib/all-off.mjs +16 -0
  75. package/lib/consts.d.ts +7 -1
  76. package/lib/consts.js +17 -2
  77. package/lib/consts.mjs +23 -0
  78. package/lib/default-methods.d.ts +12 -4
  79. package/lib/default-methods.js +174 -22
  80. package/lib/default-methods.mjs +661 -0
  81. package/lib/event-emitter.mjs +12 -0
  82. package/lib/event.d.ts +10 -4
  83. package/lib/event.js +6 -0
  84. package/lib/event.mjs +67 -0
  85. package/lib/eventable.d.ts +1 -1
  86. package/lib/eventable.mjs +90 -0
  87. package/lib/has-listeners.d.ts +1 -1
  88. package/lib/has-listeners.mjs +27 -0
  89. package/lib/index.d.ts +1 -0
  90. package/lib/index.js +12 -0
  91. package/lib/index.mjs +16 -0
  92. package/lib/once-promise.d.ts +23 -0
  93. package/lib/once-promise.js +82 -0
  94. package/lib/once-promise.mjs +80 -0
  95. package/lib/pipe-async.d.ts +4 -2
  96. package/lib/pipe-async.js +16 -3
  97. package/lib/pipe-async.mjs +100 -0
  98. package/lib/pipe.d.ts +1 -1
  99. package/lib/pipe.mjs +53 -0
  100. package/lib/unify.js +1 -1
  101. package/lib/unify.mjs +58 -0
  102. package/lib/util/array-remove.mjs +16 -0
  103. package/lib/util/object-for-each.mjs +20 -0
  104. package/lib/util/promise-any.mjs +45 -0
  105. package/lib/util/string-pad.mjs +19 -0
  106. package/lib/util/to-int.mjs +9 -0
  107. package/lib/util/valid-callable.mjs +6 -0
  108. package/lib/util/valid-object.mjs +8 -0
  109. package/lib/wrap-event-emitter.d.ts +1 -1
  110. package/lib/wrap-event-emitter.mjs +46 -0
  111. package/package.json +29 -20
  112. package/scripts/copy-to-mjs.mjs +79 -0
  113. package/src/consts.js +10 -0
  114. package/src/default-methods.js +169 -24
  115. package/src/event.js +6 -0
  116. package/src/index.js +1 -0
  117. package/src/once-promise.js +80 -0
  118. package/src/pipe-async.js +16 -3
  119. package/src/unify.js +1 -1
  120. package/tsconfig.json +2 -1
  121. package/vitest.config.js +61 -0
  122. package/.mocharc.yml +0 -3
package/lib/consts.d.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Creates an AbortError with name 'AbortError'.
3
+ * Ensures a fresh Error instance each call for proper stack traces.
4
+ * @returns {Error}
5
+ */
6
+ export function createAbortError(): Error;
1
7
  export namespace states {
2
8
  export { CONTINUE };
3
9
  export { DONE };
@@ -5,7 +11,7 @@ export namespace states {
5
11
  export { ABORT };
6
12
  }
7
13
  export const RegExpEventSymbol: symbol | "@@RegExpEvent";
8
- declare const CONTINUE: any;
14
+ declare const CONTINUE: undefined;
9
15
  declare const DONE: 1;
10
16
  declare const STOPPED: 2;
11
17
  declare const ABORT: -1;
package/lib/consts.js CHANGED
@@ -3,7 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.states = exports.RegExpEventSymbol = void 0;
6
+ exports.RegExpEventSymbol = void 0;
7
+ exports.createAbortError = createAbortError;
8
+ exports.states = void 0;
7
9
  const CONTINUE = undefined;
8
10
  const DONE = 1;
9
11
  const STOPPED = 2;
@@ -14,4 +16,17 @@ const states = exports.states = {
14
16
  STOPPED,
15
17
  ABORT
16
18
  };
17
- const RegExpEventSymbol = exports.RegExpEventSymbol = typeof Symbol === 'function' ? Symbol('RegExpEvent') : '@@RegExpEvent';
19
+
20
+ /* v8 ignore next — false branch only reachable without Symbol, which is impossible in Node.js */
21
+ const RegExpEventSymbol = exports.RegExpEventSymbol = typeof Symbol === 'function' ? Symbol('RegExpEvent') : '@@RegExpEvent';
22
+
23
+ /**
24
+ * Creates an AbortError with name 'AbortError'.
25
+ * Ensures a fresh Error instance each call for proper stack traces.
26
+ * @returns {Error}
27
+ */
28
+ function createAbortError() {
29
+ return Object.assign(new Error('The operation was aborted'), {
30
+ name: 'AbortError'
31
+ });
32
+ }
package/lib/consts.mjs ADDED
@@ -0,0 +1,23 @@
1
+ const CONTINUE = undefined
2
+ const DONE = 1
3
+ const STOPPED = 2
4
+ const ABORT = -1
5
+
6
+ export const states = {
7
+ CONTINUE,
8
+ DONE,
9
+ STOPPED,
10
+ ABORT,
11
+ }
12
+
13
+ /* v8 ignore next — false branch only reachable without Symbol, which is impossible in Node.js */
14
+ export const RegExpEventSymbol = typeof Symbol === 'function' ? Symbol('RegExpEvent') : '@@RegExpEvent'
15
+
16
+ /**
17
+ * Creates an AbortError with name 'AbortError'.
18
+ * Ensures a fresh Error instance each call for proper stack traces.
19
+ * @returns {Error}
20
+ */
21
+ export function createAbortError() {
22
+ return Object.assign(new Error('The operation was aborted'), { name: 'AbortError' })
23
+ }
@@ -4,11 +4,19 @@ export function getEventableMethods(aClass: any): {
4
4
  * @param {Object} options - Configuration options for event emission.
5
5
  * @param {string} [options.asyncMode='serial'] - The mode of asynchronous emission ('serial' or 'parallel').
6
6
  * @param {string} [options.resultMode='last'] - The strategy for handling multiple return values ('last', 'first', 'collect').
7
+ * @param {AbortSignal} [options.signal] - An AbortSignal to cancel async event emission.
8
+ * @param {boolean|null} [options.raiseError] - Controls error handling behavior:
9
+ * - `true`: Always throw listener errors immediately.
10
+ * - `false`: Silently swallow listener errors.
11
+ * - `null`: Throw only for 'error' events with no error listeners (Node.js default).
12
+ * - `undefined` (default): Same as `false` for emitAsync.
7
13
  * @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
8
14
  */
9
15
  configure(options: {
10
- asyncMode?: string;
11
- resultMode?: string;
16
+ asyncMode?: string | undefined;
17
+ resultMode?: string | undefined;
18
+ signal?: any;
19
+ raiseError?: boolean | null | undefined;
12
20
  }): import("./event-emitter").EventEmitter;
13
21
  /**
14
22
  * A shortcut for parallel configuration.
@@ -18,10 +26,10 @@ export function getEventableMethods(aClass: any): {
18
26
  parallel(resultMode?: string): import("./event-emitter").EventEmitter;
19
27
  /**
20
28
  * Sets the configuration options for the EventEmitter instance.
21
- * @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners).
29
+ * @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners, raiseError).
22
30
  * @returns {import('./event-emitter').EventEmitter} The EventEmitter instance for chaining.
23
31
  */
24
- setEmitterOptions(options: any): import("./event-emitter").EventEmitter;
32
+ setEmitterOptions(options: Object): import("./event-emitter").EventEmitter;
25
33
  /**
26
34
  * Adds a listener function to the specified event type.
27
35
  * @param {string|RegExp} type - The event type to listen for.
@@ -23,6 +23,12 @@ function getEventableMethods(aClass) {
23
23
  * @param {Object} options - Configuration options for event emission.
24
24
  * @param {string} [options.asyncMode='serial'] - The mode of asynchronous emission ('serial' or 'parallel').
25
25
  * @param {string} [options.resultMode='last'] - The strategy for handling multiple return values ('last', 'first', 'collect').
26
+ * @param {AbortSignal} [options.signal] - An AbortSignal to cancel async event emission.
27
+ * @param {boolean|null} [options.raiseError] - Controls error handling behavior:
28
+ * - `true`: Always throw listener errors immediately.
29
+ * - `false`: Silently swallow listener errors.
30
+ * - `null`: Throw only for 'error' events with no error listeners (Node.js default).
31
+ * - `undefined` (default): Same as `false` for emitAsync.
26
32
  * @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
27
33
  */
28
34
  configure(options) {
@@ -44,7 +50,7 @@ function getEventableMethods(aClass) {
44
50
  },
45
51
  /**
46
52
  * Sets the configuration options for the EventEmitter instance.
47
- * @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners).
53
+ * @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners, raiseError).
48
54
  * @returns {import('./event-emitter').EventEmitter} The EventEmitter instance for chaining.
49
55
  */
50
56
  setEmitterOptions(options) {
@@ -68,7 +74,7 @@ function getEventableMethods(aClass) {
68
74
  * Adds a listener function to the specified event type.
69
75
  * @param {string|RegExp} type - The event type to listen for.
70
76
  * @param {Function} listener - The listener function to be called when the event is emitted.
71
- * @param {number|'first'|'last'} [index] - The index at which to insert the listener.
77
+ * @param {number|'first'|'last'} [index] - The index at which to insert the listener.
72
78
  * - 'first' or -Infinity: Adds to the "Head" zone. The first listener added as 'first' is placed at the very front.
73
79
  * - 'last' or Infinity: Adds to the "Tail" zone. The first listener added as 'last' will always be the very last one to execute.
74
80
  * - number: Inserts at the specified index within the "Body" (normal) zone.
@@ -95,8 +101,8 @@ function getEventableMethods(aClass) {
95
101
  if (isRegExp(type)) {
96
102
  data = data[_consts.RegExpEventSymbol] || (data[_consts.RegExpEventSymbol] = create(null));
97
103
  }
98
- const isFirst = index === 'first' || index === -Infinity;
99
- const isLast = index === 'last' || index === Infinity;
104
+ const isFirst = index === -Infinity || index === 'first';
105
+ const isLast = index === Infinity || index === 'last';
100
106
  if (!data[type]) {
101
107
  if (isFirst || isLast || typeof index === 'number') {
102
108
  data[type] = [listener];
@@ -158,11 +164,14 @@ function getEventableMethods(aClass) {
158
164
  }
159
165
  let fired = false;
160
166
  const self = this;
161
- async function _once() {
167
+ function _once() {
162
168
  self.off(type, _once);
163
169
  if (!fired) {
164
170
  fired = true;
165
- await listener.apply(this, arguments);
171
+ const result = listener.apply(this, arguments);
172
+ if (result instanceof Promise) {
173
+ return result;
174
+ }
166
175
  }
167
176
  }
168
177
  _once.listener = listener;
@@ -184,6 +193,8 @@ function getEventableMethods(aClass) {
184
193
  const listeners = r.listeners;
185
194
  const evt = (0, _event.Event)(this, r.type);
186
195
  const errs = [];
196
+ const opts = _getOptions(this);
197
+ let _throwErr;
187
198
  try {
188
199
  let i = 0;
189
200
  let listener;
@@ -194,6 +205,9 @@ function getEventableMethods(aClass) {
194
205
  break;
195
206
  }
196
207
  } catch (err) {
208
+ if (opts.raiseError === true) {
209
+ throw err;
210
+ }
197
211
  errs.push({
198
212
  err: err,
199
213
  listener: listener
@@ -204,13 +218,20 @@ function getEventableMethods(aClass) {
204
218
  if (errs.length) {
205
219
  for (let i = 0; i < errs.length; i++) {
206
220
  const it = errs[i];
221
+ if (r.type === 'error') {
222
+ throw it.err;
223
+ }
207
224
  this.emit('error', it.err, 'notify', r.type, it.listener, args);
208
225
  }
209
226
  }
210
227
  } finally {
211
- // eslint-disable-next-line no-unsafe-finally
212
- return evt.end();
228
+ if (r.type === 'error' && opts.raiseError === null) {
229
+ const err = args[0];
230
+ _throwErr = err instanceof Error ? err : new Error(UnCAUGHT_ERR);
231
+ }
213
232
  }
233
+ if (_throwErr) throw _throwErr;
234
+ return evt.end();
214
235
  },
215
236
  /**
216
237
  * Asynchronously emits the specified event type with the given arguments.
@@ -219,6 +240,10 @@ function getEventableMethods(aClass) {
219
240
  */
220
241
  async emitAsync(/* type, msg , ... */
221
242
  ) {
243
+ const options = Object.assign({}, this._emitterOptions, this._eeRuntimeOptions);
244
+ if (options.signal && options.signal.aborted) {
245
+ throw (0, _consts.createAbortError)();
246
+ }
222
247
  const r = _emit.apply(this, arguments);
223
248
  if (!r) {
224
249
  return;
@@ -226,13 +251,29 @@ function getEventableMethods(aClass) {
226
251
  const args = r.args;
227
252
  const listeners = r.listeners;
228
253
  const evt = (0, _event.Event)(this, r.type);
229
- const options = Object.assign({}, this._emitterOptions, this._eeRuntimeOptions);
254
+ let _throwErr;
230
255
  try {
231
256
  await _executeAsync.call(this, listeners, evt, args, options);
232
- } finally {
233
- // eslint-disable-next-line no-unsafe-finally
257
+ } catch (err) {
258
+ if (err && err.name === 'AbortError') throw err;
259
+ if (options.raiseError === true) throw err;
260
+ if (r.type === 'error') {
261
+ Object.defineProperty(err, '_errorInError', {
262
+ value: true,
263
+ enumerable: false
264
+ });
265
+ throw err;
266
+ }
267
+ if (err && err._errorInError) throw err;
268
+ // Other unexpected errors: still return the event result
234
269
  return evt.end();
235
270
  }
271
+ if (r.type === 'error' && options.raiseError === null) {
272
+ const err = args[0];
273
+ _throwErr = err instanceof Error ? err : new Error(UnCAUGHT_ERR);
274
+ }
275
+ if (_throwErr) throw _throwErr;
276
+ return evt.end();
236
277
  },
237
278
  setMaxListeners(n) {
238
279
  if (!(0, _utilEx.isNumber)(n) || n < 0 || isNaN(n)) {
@@ -408,6 +449,15 @@ function getEventableMethods(aClass) {
408
449
  };
409
450
  }
410
451
  var _default = exports.default = getEventableMethods;
452
+ /**
453
+ * Merges instance-level emitter options with per-call runtime options.
454
+ * Runtime options (set via configure()) take precedence.
455
+ * @param {Object} self - The emitter instance.
456
+ * @returns {Object} Merged options.
457
+ */
458
+ function _getOptions(self) {
459
+ return Object.assign({}, self._emitterOptions, self._eeRuntimeOptions);
460
+ }
411
461
  function _emit(type, msg) {
412
462
  const data = this._events;
413
463
  let listeners;
@@ -423,8 +473,26 @@ function _emit(type, msg) {
423
473
  args.push(msg);
424
474
  }
425
475
  }
476
+ if (type === 'error') {
477
+ const opts = _getOptions(this);
478
+ // raiseError: true → always throw, bypass listener dispatch
479
+ if (opts.raiseError === true) {
480
+ /* v8 ignore next — msg is guaranteed truthy here; falsy case is caught by earlier !msg check */
481
+ if (!(msg instanceof Error)) {
482
+ msg = new Error(msg ? UnCAUGHT_ERR + msg : UnCAUGHT_ERR);
483
+ }
484
+ throw msg;
485
+ }
486
+ }
426
487
  // If there is no 'error' event listener then throw.
427
488
  if (!listeners && type === 'error') {
489
+ const opts = _getOptions(this);
490
+ // raiseError: false → silently return, never throw
491
+ if (opts.raiseError === false) {
492
+ return;
493
+ }
494
+ // raiseError: undefined / null → Node.js default behavior
495
+ /* v8 ignore next — msg is guaranteed truthy here; falsy case is caught by earlier !msg check */
428
496
  if (!(msg instanceof Error)) {
429
497
  msg = new Error(msg ? UnCAUGHT_ERR + msg : UnCAUGHT_ERR);
430
498
  }
@@ -481,9 +549,56 @@ function _notify(listener, evt, args) {
481
549
  }
482
550
  return result;
483
551
  }
552
+
553
+ /**
554
+ * Creates a promise that rejects with AbortError when the given AbortSignal fires.
555
+ * Sets evt.aborted = true before rejecting.
556
+ * @param {AbortSignal} signal - The abort signal to listen on.
557
+ * @param {import('./event').Event} evt - The event object to mark as aborted.
558
+ * @returns {Promise<never>} A promise that rejects with AbortError on abort.
559
+ */
560
+ function _createAbortPromise(signal, evt) {
561
+ return new Promise((_, reject) => {
562
+ if (signal.aborted) {
563
+ evt.aborted = true;
564
+ reject((0, _consts.createAbortError)());
565
+ return;
566
+ }
567
+ const onAbort = () => {
568
+ evt.aborted = true;
569
+ reject((0, _consts.createAbortError)());
570
+ };
571
+ signal.addEventListener('abort', onAbort, {
572
+ once: true
573
+ });
574
+ });
575
+ }
576
+
577
+ /**
578
+ * Executes all listener promises in parallel and assigns the result to evt.
579
+ * @param {Promise[]} promises - The listener promises to execute.
580
+ * @param {import('./event').Event} evt - The event object.
581
+ * @param {string} resultMode - 'last', 'first', or 'collect'.
582
+ */
583
+ async function _runParallelListeners(promises, evt, resultMode) {
584
+ if (resultMode === 'collect') {
585
+ const wrapped = promises.map(p => p.catch(() => undefined));
586
+ const results = await Promise.all(wrapped);
587
+ evt.result = results;
588
+ } else if (resultMode === 'first') {
589
+ try {
590
+ await Promise.any(promises.map(p => p.then(res => res === undefined ? Promise.reject() : res)));
591
+ } catch (e) {
592
+ // If all rejected or returned undefined, ignore
593
+ }
594
+ } else {
595
+ await Promise.all(promises.map(p => p.catch(() => undefined)));
596
+ }
597
+ }
484
598
  async function _executeAsync(listeners, evt, args, options) {
485
599
  const asyncMode = options.asyncMode || 'serial';
486
600
  const resultMode = options.resultMode || 'last';
601
+ const signal = options.signal;
487
602
  const errs = [];
488
603
  if (resultMode === 'collect') {
489
604
  evt.result = [];
@@ -501,6 +616,9 @@ async function _executeAsync(listeners, evt, args, options) {
501
616
  }
502
617
  return result;
503
618
  } catch (err) {
619
+ if (options.raiseError === true) {
620
+ throw err;
621
+ }
504
622
  errs.push({
505
623
  err,
506
624
  listener
@@ -510,27 +628,44 @@ async function _executeAsync(listeners, evt, args, options) {
510
628
  };
511
629
  if (asyncMode === 'parallel') {
512
630
  const promises = listeners.map(listener => notifyListener(listener));
513
- if (resultMode === 'collect') {
514
- evt.result = await Promise.all(promises.map(p => p.catch(() => undefined)));
515
- } else if (resultMode === 'first') {
516
- try {
517
- await Promise.any(promises.map(p => p.then(res => res === undefined ? Promise.reject() : res)));
518
- } catch (e) {
519
- // If all rejected or returned undefined, ignore
631
+ if (options.raiseError === true) {
632
+ // Collect all rejections into AggregateError (all parallel listeners start simultaneously)
633
+ const settled = signal ? await Promise.race([Promise.allSettled(promises), _createAbortPromise(signal, evt)]) : await Promise.allSettled(promises);
634
+ const rejections = settled.filter(r => r.status === 'rejected').map(r => r.reason);
635
+ if (rejections.length > 0) {
636
+ throw rejections.length === 1 ? rejections[0] : new AggregateError(rejections);
520
637
  }
521
638
  } else {
522
- await Promise.all(promises.map(p => p.catch(() => undefined)));
639
+ const runParallel = () => _runParallelListeners(promises, evt, resultMode);
640
+ if (signal) {
641
+ await Promise.race([runParallel(), _createAbortPromise(signal, evt)]);
642
+ } else {
643
+ await runParallel();
644
+ }
523
645
  }
524
646
  } else {
525
- // Serial mode (default)
647
+ // Serial mode
648
+ // Only race against abort signal when raiseError is explicitly set;
649
+ // default (undefined) keeps old behavior: check signal between listeners only.
650
+ const canRaceAbort = signal && 'raiseError' in options;
526
651
  for (const listener of listeners) {
652
+ // Check AbortSignal before firing the next listener
653
+ if (signal && signal.aborted) {
654
+ evt.aborted = true;
655
+ break;
656
+ }
527
657
  try {
528
- const result = await notifyListener(listener);
658
+ const result = canRaceAbort ? await Promise.race([notifyListener(listener), _createAbortPromise(signal, evt)]) : await notifyListener(listener);
529
659
  if (resultMode === 'collect') {
530
660
  evt.result.push(result);
531
661
  }
532
662
  if (evt.stopped || resultMode === 'first' && evt.resolved) break;
533
663
  } catch (err) {
664
+ if (canRaceAbort && err && err.name === 'AbortError') {
665
+ evt.aborted = true;
666
+ throw err;
667
+ }
668
+ if (options.raiseError === true) throw err;
534
669
  if (resultMode === 'collect') {
535
670
  evt.result.push(undefined);
536
671
  }
@@ -540,7 +675,24 @@ async function _executeAsync(listeners, evt, args, options) {
540
675
  if (errs.length) {
541
676
  for (let i = 0; i < errs.length; i++) {
542
677
  const it = errs[i];
543
- this.emit('error', it.err, 'notify', evt.type, it.listener, args);
678
+ if (evt.type === 'error') {
679
+ throw it.err;
680
+ }
681
+ // Temporarily clear signal so error re-emission is not blocked by an already-aborted signal
682
+ const savedSignal = this._eeRuntimeOptions && this._eeRuntimeOptions.signal;
683
+ if (savedSignal) {
684
+ delete this._eeRuntimeOptions.signal;
685
+ }
686
+ try {
687
+ await this.emitAsync('error', it.err, 'notify', evt.type, it.listener, args);
688
+ } finally {
689
+ if (savedSignal) {
690
+ this._eeRuntimeOptions.signal = savedSignal;
691
+ }
692
+ }
544
693
  }
545
694
  }
695
+ if (evt.aborted) {
696
+ throw (0, _consts.createAbortError)();
697
+ }
546
698
  }