events-ex 2.1.1 → 2.3.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 (98) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.cn.md +119 -170
  3. package/README.md +140 -150
  4. package/docs/README.md +143 -149
  5. package/docs/all-off/README.md +17 -0
  6. package/docs/all-off/functions/allOff.md +33 -0
  7. package/docs/consts/README.md +12 -0
  8. package/docs/consts/variables/RegExpEventSymbol.md +11 -0
  9. package/docs/consts/variables/states.md +29 -0
  10. package/docs/default-methods/README.md +17 -0
  11. package/docs/default-methods/functions/getEventableMethods.md +309 -0
  12. package/docs/event/README.md +17 -0
  13. package/docs/event/classes/Event.md +143 -0
  14. package/docs/event-emitter/README-1.md +17 -0
  15. package/docs/event-emitter/README.md +17 -0
  16. package/docs/event-emitter/classes/EventEmitter-1.md +29 -0
  17. package/docs/event-emitter/classes/EventEmitter.md +369 -0
  18. package/docs/eventable/README.md +17 -0
  19. package/docs/eventable/functions/eventable.md +82 -0
  20. package/docs/has-listeners/README.md +17 -0
  21. package/docs/has-listeners/functions/hasListeners.md +38 -0
  22. package/docs/index/README.md +85 -0
  23. package/docs/modules.md +24 -23
  24. package/docs/pipe/README.md +17 -0
  25. package/docs/pipe/functions/pipe.md +41 -0
  26. package/docs/pipe-async/README.md +17 -0
  27. package/docs/pipe-async/functions/pipeAsync.md +41 -0
  28. package/docs/unify/README.md +17 -0
  29. package/docs/unify/functions/unify.md +35 -0
  30. package/docs/util/array-remove/README.md +17 -0
  31. package/docs/util/array-remove/functions/remove.md +21 -0
  32. package/docs/util/object-for-each/README.md +17 -0
  33. package/docs/util/object-for-each/functions/forEach.md +29 -0
  34. package/docs/util/promise-any/README.md +11 -0
  35. package/docs/util/promise-any/variables/default.md +9 -0
  36. package/docs/util/string-pad/README.md +17 -0
  37. package/docs/util/string-pad/functions/pad.md +25 -0
  38. package/docs/util/to-int/README.md +17 -0
  39. package/docs/util/to-int/functions/toInt.md +21 -0
  40. package/docs/util/valid-callable/README.md +17 -0
  41. package/docs/util/valid-callable/functions/validCallable.md +21 -0
  42. package/docs/util/valid-object/README.md +17 -0
  43. package/docs/util/valid-object/functions/validObject.md +21 -0
  44. package/docs/wrap-event-emitter/README.md +21 -0
  45. package/docs/wrap-event-emitter/functions/wrapEventEmitter.md +33 -0
  46. package/docs/wrap-event-emitter/variables/methods.md +11 -0
  47. package/lib/all-off.d.ts +1 -1
  48. package/lib/all-off.js +1 -1
  49. package/lib/default-methods.d.ts +38 -7
  50. package/lib/default-methods.js +155 -32
  51. package/lib/event-emitter.d.ts +12 -2
  52. package/lib/event.d.ts +9 -3
  53. package/lib/event.js +6 -0
  54. package/lib/eventable.js +4 -1
  55. package/lib/pipe-async.d.ts +4 -1
  56. package/lib/pipe-async.js +39 -5
  57. package/lib/pipe.d.ts +1 -1
  58. package/lib/pipe.js +1 -1
  59. package/lib/unify.d.ts +1 -1
  60. package/lib/unify.js +1 -1
  61. package/lib/util/array-remove.js +1 -1
  62. package/lib/util/object-for-each.js +1 -1
  63. package/lib/util/promise-any.d.ts +1 -0
  64. package/lib/util/promise-any.js +44 -0
  65. package/lib/util/string-pad.js +1 -1
  66. package/lib/wrap-event-emitter.d.ts +2 -1
  67. package/lib/wrap-event-emitter.js +24 -3
  68. package/package.json +17 -17
  69. package/src/default-methods.js +159 -26
  70. package/src/event-emitter.d.ts +12 -2
  71. package/src/event.js +6 -0
  72. package/src/eventable.js +3 -0
  73. package/src/pipe-async.js +64 -30
  74. package/src/util/promise-any.js +45 -0
  75. package/src/wrap-event-emitter.js +23 -18
  76. package/docs/.nojekyll +0 -1
  77. package/docs/classes/event.Event.md +0 -141
  78. package/docs/classes/event_emitter-1.EventEmitter.md +0 -29
  79. package/docs/classes/event_emitter.EventEmitter.md +0 -352
  80. package/docs/modules/all_off.md +0 -44
  81. package/docs/modules/consts.md +0 -39
  82. package/docs/modules/default_methods.md +0 -51
  83. package/docs/modules/event.md +0 -19
  84. package/docs/modules/event_emitter-1.md +0 -19
  85. package/docs/modules/event_emitter.md +0 -19
  86. package/docs/modules/eventable.md +0 -92
  87. package/docs/modules/has_listeners.md +0 -49
  88. package/docs/modules/index.md +0 -99
  89. package/docs/modules/pipe.md +0 -49
  90. package/docs/modules/pipe_async.md +0 -49
  91. package/docs/modules/unify.md +0 -46
  92. package/docs/modules/util_array_remove.md +0 -39
  93. package/docs/modules/util_object_for_each.md +0 -41
  94. package/docs/modules/util_string_pad.md +0 -40
  95. package/docs/modules/util_to_int.md +0 -39
  96. package/docs/modules/util_valid_callable.md +0 -39
  97. package/docs/modules/util_valid_object.md +0 -39
  98. package/docs/modules/wrap_event_emitter.md +0 -57
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "events-ex",
3
- "version": "2.1.1",
3
+ "version": "2.3.0",
4
4
  "description": "Browser-friendly enhanced events most compatible with standard node.js, it's powerful eventable ability.",
5
5
  "contributors": [
6
6
  {
@@ -37,25 +37,25 @@
37
37
  "main": "./lib/index.js",
38
38
  "module": "./src/index.js",
39
39
  "dependencies": {
40
- "custom-ability": "^2.0.1",
41
- "util-ex": "^2.0.0"
40
+ "custom-ability": "^2.1.0",
41
+ "util-ex": "^2.5.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@antfu/eslint-config": "^2.6.1",
45
- "@babel/cli": "^7.23.4",
46
- "@babel/core": "^7.23.7",
44
+ "@antfu/eslint-config": "^7.7.0",
45
+ "@babel/cli": "^7.28.6",
46
+ "@babel/core": "^7.29.0",
47
47
  "@babel/plugin-proposal-dynamic-import": "^7.18.6",
48
- "@babel/plugin-transform-modules-commonjs": "^7.23.3",
49
- "@babel/register": "^7.23.7",
50
- "chai": "~4.3.7",
51
- "eslint": "^8.56.0",
52
- "eslint-config-prettier": "^9.1.0",
53
- "eslint-plugin-tsdoc": "^0.2.17",
54
- "mocha": "^10.6.0",
55
- "prettier": "^3.1.1",
56
- "typedoc": "^0.25.4",
57
- "typedoc-plugin-markdown": "^3.17.1",
58
- "typescript": "^5.3.3"
48
+ "@babel/plugin-transform-modules-commonjs": "^7.28.6",
49
+ "@babel/register": "^7.28.6",
50
+ "chai": "~4.3.10",
51
+ "eslint": "^10.0.3",
52
+ "eslint-config-prettier": "^10.1.8",
53
+ "eslint-plugin-tsdoc": "^0.5.2",
54
+ "mocha": "^11.7.5",
55
+ "prettier": "^3.8.1",
56
+ "typedoc": "^0.28.17",
57
+ "typedoc-plugin-markdown": "^4.10.0",
58
+ "typescript": "~5.7.3"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "npm run build.cjs && npm run build.ts && npm run doc.md",
@@ -1,4 +1,5 @@
1
1
  import {defineProperty, isArray, isFunction, isNumber, isObject, isRegExp as _isRegExp, isUndefined, isRegExpStr, toRegExp } from 'util-ex'
2
+ import './util/promise-any'
2
3
  import {RegExpEventSymbol} from './consts'
3
4
  import {Event} from './event';
4
5
 
@@ -13,11 +14,59 @@ function isRegExp(value) {
13
14
 
14
15
  export function getEventableMethods(aClass) {
15
16
  return {
17
+ /**
18
+ * Configures the event emitter with specified options using a Fluent API.
19
+ * @param {Object} options - Configuration options for event emission.
20
+ * @param {string} [options.asyncMode='serial'] - The mode of asynchronous emission ('serial' or 'parallel').
21
+ * @param {string} [options.resultMode='last'] - The strategy for handling multiple return values ('last', 'first', 'collect').
22
+ * @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
23
+ */
24
+ configure(options) {
25
+ const proxy = Object.create(this)
26
+ const mergedOptions = Object.assign({}, this._eeRuntimeOptions, options)
27
+ defineProperty(proxy, '_eeRuntimeOptions', mergedOptions)
28
+ return proxy
29
+ },
30
+
31
+ /**
32
+ * A shortcut for parallel configuration.
33
+ * @param {string} [resultMode='last'] - The strategy for handling multiple return values.
34
+ * @returns {import('./event-emitter').EventEmitter} A proxy object representing the configured EventEmitter.
35
+ */
36
+ parallel(resultMode) {
37
+ return this.configure({asyncMode: 'parallel', resultMode: resultMode || 'last'})
38
+ },
39
+
40
+ /**
41
+ * Sets the configuration options for the EventEmitter instance.
42
+ * @param {Object} options - Configuration options for the emitter (e.g., asyncMode, resultMode, maxListeners).
43
+ * @returns {import('./event-emitter').EventEmitter} The EventEmitter instance for chaining.
44
+ */
45
+ setEmitterOptions(options) {
46
+ if (!isObject(options)) {return this}
47
+ let data
48
+ if (!this.hasOwnProperty('_emitterOptions')) {
49
+ data = create(null)
50
+ defineProperty(this, '_emitterOptions', data)
51
+ } else {
52
+ data = this._emitterOptions
53
+ }
54
+ Object.assign(data, options)
55
+ if (!isUndefined(options.maxListeners) && isFunction(this.setMaxListeners)) {
56
+ this.setMaxListeners(options.maxListeners)
57
+ }
58
+ return this
59
+ },
60
+
16
61
  /**
17
62
  * Adds a listener function to the specified event type.
18
63
  * @param {string|RegExp} type - The event type to listen for.
19
64
  * @param {Function} listener - The listener function to be called when the event is emitted.
20
- * @param {number} [index] - The index at which to insert the listener. If not specified, the listener will be added at the end of the listeners array.
65
+ * @param {number|'first'|'last'} [index] - The index at which to insert the listener.
66
+ * - 'first' or -Infinity: Adds to the "Head" zone. The first listener added as 'first' is placed at the very front.
67
+ * - 'last' or Infinity: Adds to the "Tail" zone. The first listener added as 'last' will always be the very last one to execute.
68
+ * - number: Inserts at the specified index within the "Body" (normal) zone.
69
+ * If not specified, the listener is added to the end of the "Body" zone.
21
70
  * @returns {import('./event-emitter').EventEmitter} The EventEmitter instance to allow chaining.
22
71
  * @throws {TypeError} If the listener is not a function.
23
72
  */
@@ -38,16 +87,38 @@ export function getEventableMethods(aClass) {
38
87
  if (isRegExp(type)) {
39
88
  data = data[RegExpEventSymbol] || (data[RegExpEventSymbol] = create(null))
40
89
  }
41
- if (!data[type]) {
42
- data[type] = listener
43
- } else if (isObject(data[type])) {
44
- if (typeof index === 'number') {
45
- data[type].splice(index, 0, listener)
90
+
91
+ const isFirst = index === 'first' || index === -Infinity
92
+ const isLast = index === 'last' || index === Infinity
93
+
94
+ if (!data[type]) {
95
+ if (isFirst || isLast || typeof index === 'number') {
96
+ data[type] = [listener]
97
+ if (isFirst) data[type]._headCount = 1
98
+ if (isLast) data[type]._tailCount = 1
46
99
  } else {
47
- data[type].push(listener)
100
+ data[type] = listener
48
101
  }
49
102
  } else {
50
- data[type] = [data[type], listener]
103
+ if (isFunction(data[type])) {
104
+ data[type] = [data[type]]
105
+ }
106
+ const listeners = data[type]
107
+ const headCount = listeners._headCount || 0
108
+ const tailCount = listeners._tailCount || 0
109
+
110
+ if (isFirst) {
111
+ listeners.splice(headCount, 0, listener)
112
+ listeners._headCount = headCount + 1
113
+ } else if (isLast) {
114
+ listeners.splice(listeners.length - tailCount, 0, listener)
115
+ listeners._tailCount = tailCount + 1
116
+ } else if (typeof index === 'number' && !isNaN(index)) {
117
+ const pos = Math.min(Math.max(headCount + index, headCount), listeners.length - tailCount)
118
+ listeners.splice(pos, 0, listener)
119
+ } else {
120
+ listeners.splice(listeners.length - tailCount, 0, listener)
121
+ }
51
122
  }
52
123
  // Check for listener leak
53
124
  if (isObject(data[type]) && !data[type].warned) {
@@ -74,7 +145,11 @@ export function getEventableMethods(aClass) {
74
145
  * Adds a one-time listener function to the specified event type.
75
146
  * @param {string|RegExp} type - The event type to listen for.
76
147
  * @param {Function} listener - The listener function to be called once when the event is emitted.
77
- * @param {number} [index] - The index at which to insert the listener. If not specified, the listener will be added at the end of the listeners array.
148
+ * @param {number|'first'|'last'} [index] - The index at which to insert the listener.
149
+ * - 'first' or -Infinity: Adds to the "Head" zone. The first listener added as 'first' is placed at the very front.
150
+ * - 'last' or Infinity: Adds to the "Tail" zone. The first listener added as 'last' will always be the very last one to execute.
151
+ * - number: Inserts at the specified index within the "Body" (normal) zone.
152
+ * If not specified, the listener is added to the end of the "Body" zone.
78
153
  * @returns {import('./event-emitter').EventEmitter} The EventEmitter instance to allow chaining.
79
154
  * @throws {TypeError} If the listener is not a function.
80
155
  */
@@ -143,22 +218,9 @@ export function getEventableMethods(aClass) {
143
218
  const args = r.args
144
219
  const listeners = r.listeners
145
220
  const evt = Event(this, r.type)
146
- const errs = []
221
+ const options = Object.assign({}, this._emitterOptions, this._eeRuntimeOptions)
147
222
  try {
148
- for (const listener of listeners) {
149
- try {
150
- await _notify(listener, evt, args);
151
- if (evt.stopped) {break}
152
- } catch(err) {
153
- errs.push({err: err, listener: listener})
154
- }
155
- }
156
- if (errs.length) {
157
- for (let i=0;i<errs.length;i++) {
158
- const it = errs[i]
159
- this.emit('error', it.err, 'notify', r.type, it.listener, args)
160
- }
161
- }
223
+ await _executeAsync.call(this, listeners, evt, args, options)
162
224
  } finally {
163
225
  // eslint-disable-next-line no-unsafe-finally
164
226
  return evt.end()
@@ -243,10 +305,16 @@ export function getEventableMethods(aClass) {
243
305
  if (candidate === listener || candidate.listener === listener) {break}
244
306
  }
245
307
  if (i < 0) {return this}
308
+
309
+ if (listeners._headCount && i < listeners._headCount) {
310
+ listeners._headCount--
311
+ } else if (listeners._tailCount && i >= listeners.length - listeners._tailCount) {
312
+ listeners._tailCount--
313
+ }
314
+
246
315
  if (listeners.length === 1) {
247
- listeners.length = 0
248
316
  delete data[type]
249
- } else if (listeners.length === 2) {
317
+ } else if (listeners.length === 2 && !listeners._headCount && !listeners._tailCount) {
250
318
  data[type] = listeners[(i ? 0 : 1)]
251
319
  listeners.length = 1
252
320
  } else {
@@ -381,3 +449,68 @@ function _notify(listener, evt, args) {
381
449
  }
382
450
  return result
383
451
  }
452
+
453
+ async function _executeAsync(listeners, evt, args, options) {
454
+ const asyncMode = options.asyncMode || 'serial'
455
+ const resultMode = options.resultMode || 'last'
456
+ const errs = []
457
+
458
+ if (resultMode === 'collect') {
459
+ evt.result = []
460
+ }
461
+
462
+ const notifyListener = async (listener) => {
463
+ try {
464
+ const result = await _notify(listener, evt, args)
465
+ if (result !== undefined) {
466
+ if (resultMode === 'first' && !evt.resolved) {
467
+ evt.result = result
468
+ evt.resolved = true
469
+ } else if (resultMode === 'last') {
470
+ evt.result = result
471
+ }
472
+ }
473
+ return result
474
+ } catch (err) {
475
+ errs.push({err, listener})
476
+ throw err
477
+ }
478
+ }
479
+
480
+ if (asyncMode === 'parallel') {
481
+ const promises = listeners.map(listener => notifyListener(listener))
482
+ if (resultMode === 'collect') {
483
+ evt.result = await Promise.all(promises.map(p => p.catch(() => undefined)))
484
+ } else if (resultMode === 'first') {
485
+ try {
486
+ await Promise.any(promises.map(p => p.then(res => res === undefined ? Promise.reject() : res)))
487
+ } catch (e) {
488
+ // If all rejected or returned undefined, ignore
489
+ }
490
+ } else {
491
+ await Promise.all(promises.map(p => p.catch(() => undefined)))
492
+ }
493
+ } else {
494
+ // Serial mode (default)
495
+ for (const listener of listeners) {
496
+ try {
497
+ const result = await notifyListener(listener)
498
+ if (resultMode === 'collect') {
499
+ evt.result.push(result)
500
+ }
501
+ if (evt.stopped || (resultMode === 'first' && evt.resolved)) break
502
+ } catch (err) {
503
+ if (resultMode === 'collect') {
504
+ evt.result.push(undefined)
505
+ }
506
+ }
507
+ }
508
+ }
509
+
510
+ if (errs.length) {
511
+ for (let i = 0; i < errs.length; i++) {
512
+ const it = errs[i]
513
+ this.emit('error', it.err, 'notify', evt.type, it.listener, args)
514
+ }
515
+ }
516
+ }
@@ -10,18 +10,28 @@ export class EventEmitter {
10
10
  * Adds a listener function to the specified event type.
11
11
  * @param {string|RegExp} type - The event type to listen for.
12
12
  * @param {Function} listener - The listener function to be called when the event is emitted.
13
+ * @param {number|'first'|'last'} [index] - The index at which to insert the listener.
14
+ * - 'first' or -Infinity: adds to the beginning of the listeners (stay at the front).
15
+ * - 'last' or Infinity: adds to the end of the listeners (stay at the back).
16
+ * - number: inserts at the specified index within the normal listeners zone.
17
+ * If not specified, the listener will be added at the end of the normal listeners.
13
18
  * @returns {EventEmitter} The EventEmitter instance to allow chaining.
14
19
  * @throws {TypeError} If the listener is not a function.
15
20
  */
16
- on(eventName: string|RegExp, listener: ListenerCallbackFunc): EventEmitter;
21
+ on(eventName: string|RegExp, listener: ListenerCallbackFunc, index?: number|'first'|'last'): EventEmitter;
17
22
  /**
18
23
  * Adds a one-time listener function to the specified event type.
19
24
  * @param {string|RegExp} type - The event type to listen for.
20
25
  * @param {Function} listener - The listener function to be called once when the event is emitted.
26
+ * @param {number|'first'|'last'} [index] - The index at which to insert the listener.
27
+ * - 'first' or -Infinity: adds to the beginning of the listeners (stay at the front).
28
+ * - 'last' or Infinity: adds to the end of the listeners (stay at the back).
29
+ * - number: inserts at the specified index within the normal listeners zone.
30
+ * If not specified, the listener will be added at the end of the normal listeners.
21
31
  * @returns {EventEmitter} The EventEmitter instance to allow chaining.
22
32
  * @throws {TypeError} If the listener is not a function.
23
33
  */
24
- once(eventName: string|RegExp, listener: ListenerCallbackFunc): EventEmitter;
34
+ once(eventName: string|RegExp, listener: ListenerCallbackFunc, index?: number|'first'|'last'): EventEmitter;
25
35
  /**
26
36
  * Removes a listener function from the specified event type.
27
37
  * @param {string|RegExp} type - The event type to remove the listener from.
package/src/event.js CHANGED
@@ -30,6 +30,12 @@ Event.prototype.init = function(target, type) {
30
30
  * @public
31
31
  */
32
32
  this.stopped = false
33
+ /**
34
+ * Whether a result has been resolved (for 'first' result mode)
35
+ * @type {boolean}
36
+ * @public
37
+ */
38
+ this.resolved = false
33
39
  /**
34
40
  * Keep your event result here if any.
35
41
  * @type {*}
package/src/eventable.js CHANGED
@@ -25,6 +25,9 @@ function getEventableClass(aClass) {
25
25
  Eventable.prototype.listenerCount = methods.listenerCount;
26
26
  Eventable.prototype.emit = methods.emit;
27
27
  Eventable.prototype.emitAsync = methods.emitAsync;
28
+ Eventable.prototype.configure = methods.configure;
29
+ Eventable.prototype.parallel = methods.parallel;
30
+ Eventable.prototype.setEmitterOptions = methods.setEmitterOptions;
28
31
  Eventable.prototype.on = methods.on;
29
32
  Eventable.prototype.addListener = methods.on;
30
33
  Eventable.prototype.off = methods.off;
package/src/pipe-async.js CHANGED
@@ -15,39 +15,73 @@ const emit = methods.emitAsync
15
15
  * @param {import('./event-emitter').EventEmitter} e1 - The first event emitter.
16
16
  * @param {import('./event-emitter').EventEmitter} e2 - The second event emitter.
17
17
  * @param {string} [name='emitAsync'] - The name of the event to pipe (defaults to 'emitAsync').
18
+ * @param {Object} [options] - Configuration for the pipeline.
19
+ * @param {string} [options.asyncMode='serial'] - The mode of propagation ('serial' or 'parallel').
20
+ * @param {string} [options.resultMode] - Strategy for aggregating results from the pipe chain ('collect', 'first').
18
21
  * @returns {Object} - An object with a `close` method that removes the pipeline between the two event emitters.
19
22
  * @throws {TypeError} - If either of the arguments is not an event emitter object.
20
23
  */
21
- export function pipeAsync(e1, e2/* , name */) {
22
- let pipes
23
-
24
- (validObject(e1) && validObject(e2))
25
- let name = arguments[2]
26
- if (name === undefined) {name = 'emitAsync'}
27
-
28
- const result = {
29
- close() { arrRemove.call(pipes, e2) }
30
- };
31
- if (hasOwnProperty.call(e1, '__eePipes__')) {
32
- (pipes = e1.__eePipes__).push(e2)
33
- return result
34
- }
35
- defineProperty(e1, '__eePipes__', pipes = [e2])
36
- let desc = getOwnPropertyDescriptor(e1, name)
37
- if (!desc) {
38
- desc = {}
39
- } else {
40
- delete desc.get
41
- delete desc.set
42
- }
43
- desc.value = async function () {
44
- const data = arrFrom(pipes)
45
- await emit.apply(this, arguments)
46
- let emitter
47
- for (let i = 0; (emitter = data[i]); ++i) await emit.apply(emitter, arguments)
48
- }
49
- defineProperty(e1, name, desc.value, desc)
50
- return result
24
+ export function pipeAsync(e1, e2/* , name, options */) {
25
+ let pipes
26
+
27
+ (validObject(e1) && validObject(e2))
28
+ let name = arguments[2]
29
+ let options = arguments[3]
30
+ if (typeof name === 'object') {
31
+ options = name
32
+ name = undefined
33
+ }
34
+ if (name === undefined) {name = 'emitAsync'}
35
+ if (!options) {options = {}}
36
+
37
+ const result = {
38
+ close() { arrRemove.call(pipes, e2) }
39
+ };
40
+ if (hasOwnProperty.call(e1, '__eePipes__')) {
41
+ (pipes = e1.__eePipes__).push(e2)
42
+ return result
43
+ }
44
+ defineProperty(e1, '__eePipes__', pipes = [e2])
45
+ let desc = getOwnPropertyDescriptor(e1, name)
46
+ if (!desc) {
47
+ desc = {}
48
+ } else {
49
+ delete desc.get
50
+ delete desc.set
51
+ }
52
+ desc.value = async function () {
53
+ const data = arrFrom(pipes)
54
+ const asyncMode = options.asyncMode || 'serial'
55
+ const resultMode = options.resultMode
56
+
57
+ const forward = async (target, args) => {
58
+ const fn = target[name] || target.emitAsync || emit
59
+ return fn.apply(target, args)
60
+ }
61
+
62
+ if (asyncMode === 'parallel') {
63
+ const promises = [emit.apply(this, arguments)]
64
+ for (let i = 0; i < data.length; ++i) {
65
+ promises.push(forward(data[i], arguments))
66
+ }
67
+ const results = await Promise.all(promises)
68
+ if (resultMode === 'collect') return results
69
+ if (resultMode === 'first') return results.find(r => r !== undefined)
70
+ return results[0] // Default: return main emitter's result
71
+ } else {
72
+ const mainResult = await emit.apply(this, arguments)
73
+ const allResults = [mainResult]
74
+ for (let i = 0; i < data.length; ++i) {
75
+ const res = await forward(data[i], arguments)
76
+ allResults.push(res)
77
+ }
78
+ if (resultMode === 'collect') return allResults
79
+ if (resultMode === 'first') return allResults.find(r => r !== undefined)
80
+ return mainResult // Default: return main emitter's result
81
+ }
82
+ }
83
+ defineProperty(e1, name, desc.value, desc)
84
+ return result
51
85
  };
52
86
 
53
87
  export default pipeAsync
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Simple polyfill for AggregateError if it doesn't exist.
3
+ */
4
+ const _AggregateError = typeof AggregateError !== 'undefined'
5
+ ? AggregateError
6
+ : function AggregateError(errors, message) {
7
+ const error = new Error(message);
8
+ error.name = 'AggregateError';
9
+ error.errors = errors;
10
+ return error;
11
+ };
12
+
13
+ /**
14
+ * Ensures Promise.any exists, or provides a polyfill.
15
+ */
16
+ if (typeof Promise.any !== 'function') {
17
+ Promise.any = function (promises) {
18
+ return new Promise((resolve, reject) => {
19
+ promises = Array.from(promises);
20
+ const len = promises.length;
21
+ let errors = [];
22
+ let rejectedCount = 0;
23
+
24
+ if (len === 0) {
25
+ return reject(new _AggregateError(errors, 'All promises were rejected'));
26
+ }
27
+
28
+ promises.forEach((promise, index) => {
29
+ Promise.resolve(promise)
30
+ .then((value) => {
31
+ resolve(value);
32
+ })
33
+ .catch((error) => {
34
+ errors[index] = error;
35
+ rejectedCount++;
36
+ if (rejectedCount === len) {
37
+ reject(new _AggregateError(errors, 'All promises were rejected'));
38
+ }
39
+ });
40
+ });
41
+ });
42
+ };
43
+ }
44
+
45
+ export default Promise.any;
@@ -6,36 +6,41 @@ const defineProperties = Object.defineProperties;
6
6
 
7
7
  export const methods = eventable().methods;
8
8
 
9
+ /**
10
+ * Minimal set of core methods to be injected into an existing object.
11
+ */
9
12
  const descriptors = {
10
- on: {
11
- value: methods.on
12
- },
13
- once: {
14
- value: methods.once
15
- },
16
- off: {
17
- value: methods.off
18
- },
19
- emit: {
20
- value: methods.emit
21
- },
22
- emitAsync: {
23
- value: methods.emitAsync
24
- },
13
+ on: { value: methods.on },
14
+ once: { value: methods.once },
15
+ off: { value: methods.off },
16
+ emit: { value: methods.emit },
17
+ emitAsync: { value: methods.emitAsync },
18
+ setEmitterOptions: { value: methods.setEmitterOptions },
25
19
  };
26
20
 
27
- const base = defineProperties({}, descriptors);
21
+ /**
22
+ * Full set of all available methods from eventable for standalone instances.
23
+ */
24
+ const fullDescriptors = {};
25
+ Object.keys(methods).forEach(key => {
26
+ fullDescriptors[key] = { value: methods[key] };
27
+ });
28
+
29
+ const base = defineProperties({}, fullDescriptors);
28
30
 
29
31
  /**
30
32
  * Create or inject the eventable instance into the object
31
33
  * @param {Object} [o] the optional instance to eventable
34
+ * @param {Object} [options] optional configuration for the emitter
32
35
  * @returns o or new Event instance
33
36
  */
34
- export function wrapEventEmitter(o) {
37
+ export function wrapEventEmitter(o, options) {
35
38
  const result = o == null ? create(base) : defineProperties(Object(o), descriptors)
36
39
  defineProperty(result, '_events', {})
40
+ if (options && options.emitterOptions) {
41
+ result.setEmitterOptions(options.emitterOptions)
42
+ }
37
43
  return result
38
44
  };
39
45
 
40
-
41
46
  export default wrapEventEmitter
package/docs/.nojekyll DELETED
@@ -1 +0,0 @@
1
- TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.