UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

1,261 lines (1,121 loc) 36.1 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/events.js import * as __hoisted_async_hooks__ from "nstdlib/lib/async_hooks"; import * as __hoisted_internal_event_target__ from "nstdlib/lib/internal/event_target"; import * as __hoisted_internal_util__ from "nstdlib/lib/internal/util"; import * as __hoisted_internal_fixed_queue__ from "nstdlib/lib/internal/fixed_queue"; import * as __hoisted_internal_events_symbols__ from "nstdlib/lib/internal/events/symbols"; import { kEmptyObject } from "nstdlib/lib/internal/util"; import { inspect, identicalSequenceRange, } from "nstdlib/lib/internal/util/inspect"; import { AbortError, codes as __codes__, genericNodeError, kEnhanceStackBeforeInspector, } from "nstdlib/lib/internal/errors"; import { validateInteger, validateAbortSignal, validateBoolean, validateFunction, validateNumber, validateObject, validateString, } from "nstdlib/lib/internal/validators"; import { addAbortListener } from "nstdlib/lib/internal/events/abort_listener"; let _module_exports_ = {}; export { _module_exports_ as default }; // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. const kRejection = Symbol.for("nodejs.rejection"); let spliceOne; let FixedQueue; let kFirstEventParam; let kResistStopPropagation; const { ERR_INVALID_ARG_TYPE, ERR_INVALID_THIS, ERR_UNHANDLED_ERROR } = __codes__; const kCapture = Symbol("kCapture"); const kErrorMonitor = Symbol("events.errorMonitor"); const kShapeMode = Symbol("shapeMode"); const kMaxEventTargetListeners = Symbol("events.maxEventTargetListeners"); const kMaxEventTargetListenersWarned = Symbol( "events.maxEventTargetListenersWarned", ); const kWatermarkData = Symbol.for("nodejs.watermarkData"); let EventEmitterAsyncResource; // The EventEmitterAsyncResource has to be initialized lazily because event.js // is loaded so early in the bootstrap process, before async_hooks is available. // // This implementation was adapted straight from addaleax's // eventemitter-asyncresource MIT-licensed userland module. // https://github.com/addaleax/eventemitter-asyncresource function lazyEventEmitterAsyncResource() { if (EventEmitterAsyncResource === undefined) { const { AsyncResource } = __hoisted_async_hooks__; const kEventEmitter = Symbol("kEventEmitter"); const kAsyncResource = Symbol("kAsyncResource"); class EventEmitterReferencingAsyncResource extends AsyncResource { /** * @param {EventEmitter} ee * @param {string} [type] * @param {{ * triggerAsyncId?: number, * requireManualDestroy?: boolean, * }} [options] */ constructor(ee, type, options) { super(type, options); this[kEventEmitter] = ee; } /** * @type {EventEmitter} */ get eventEmitter() { if (this[kEventEmitter] === undefined) throw new ERR_INVALID_THIS("EventEmitterReferencingAsyncResource"); return this[kEventEmitter]; } } EventEmitterAsyncResource = class EventEmitterAsyncResource extends ( EventEmitter ) { /** * @param {{ * name?: string, * triggerAsyncId?: number, * requireManualDestroy?: boolean, * }} [options] */ constructor(options = undefined) { let name; if (typeof options === "string") { name = options; options = undefined; } else { if (new.target === EventEmitterAsyncResource) { validateString(options?.name, "options.name"); } name = options?.name || new.target.name; } super(options); this[kAsyncResource] = new EventEmitterReferencingAsyncResource( this, name, options, ); } /** * @param {symbol,string} event * @param {...any} args * @returns {boolean} */ emit(event, ...args) { if (this[kAsyncResource] === undefined) throw new ERR_INVALID_THIS("EventEmitterAsyncResource"); const { asyncResource } = this; Array.prototype.unshift.call(args, super.emit, this, event); return ReflectApply(asyncResource.runInAsyncScope, asyncResource, args); } /** * @returns {void} */ emitDestroy() { if (this[kAsyncResource] === undefined) throw new ERR_INVALID_THIS("EventEmitterAsyncResource"); this.asyncResource.emitDestroy(); } /** * @type {number} */ get asyncId() { if (this[kAsyncResource] === undefined) throw new ERR_INVALID_THIS("EventEmitterAsyncResource"); return this.asyncResource.asyncId(); } /** * @type {number} */ get triggerAsyncId() { if (this[kAsyncResource] === undefined) throw new ERR_INVALID_THIS("EventEmitterAsyncResource"); return this.asyncResource.triggerAsyncId(); } /** * @type {EventEmitterReferencingAsyncResource} */ get asyncResource() { if (this[kAsyncResource] === undefined) throw new ERR_INVALID_THIS("EventEmitterAsyncResource"); return this[kAsyncResource]; } }; } return EventEmitterAsyncResource; } /** * Creates a new `EventEmitter` instance. * @param {{ captureRejections?: boolean; }} [opts] * @constructs {EventEmitter} */ function EventEmitter(opts) { EventEmitter.init.call(this, opts); } _module_exports_ = EventEmitter; _module_exports_.addAbortListener = addAbortListener; _module_exports_.once = once; _module_exports_.on = on; _module_exports_.getEventListeners = getEventListeners; _module_exports_.getMaxListeners = getMaxListeners; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.usingDomains = false; EventEmitter.captureRejectionSymbol = kRejection; Object.defineProperty(EventEmitter, "captureRejections", { __proto__: null, get() { return EventEmitter.prototype[kCapture]; }, set(value) { validateBoolean(value, "EventEmitter.captureRejections"); EventEmitter.prototype[kCapture] = value; }, enumerable: true, }); Object.defineProperty(EventEmitter, "EventEmitterAsyncResource", { __proto__: null, enumerable: true, get: lazyEventEmitterAsyncResource, set: undefined, configurable: true, }); EventEmitter.errorMonitor = kErrorMonitor; // The default for captureRejections is false Object.defineProperty(EventEmitter.prototype, kCapture, { __proto__: null, value: false, writable: true, enumerable: false, }); EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. let defaultMaxListeners = 10; let isEventTarget; function checkListener(listener) { validateFunction(listener, "listener"); } Object.defineProperty(EventEmitter, "defaultMaxListeners", { __proto__: null, enumerable: true, get: function () { return defaultMaxListeners; }, set: function (arg) { validateNumber(arg, "defaultMaxListeners", 0); defaultMaxListeners = arg; }, }); Object.defineProperties(EventEmitter, { kMaxEventTargetListeners: { __proto__: null, value: kMaxEventTargetListeners, enumerable: false, configurable: false, writable: false, }, kMaxEventTargetListenersWarned: { __proto__: null, value: kMaxEventTargetListenersWarned, enumerable: false, configurable: false, writable: false, }, }); /** * Sets the max listeners. * @param {number} n * @param {EventTarget[] | EventEmitter[]} [eventTargets] * @returns {void} */ EventEmitter.setMaxListeners = function ( n = defaultMaxListeners, ...eventTargets ) { validateNumber(n, "setMaxListeners", 0); if (eventTargets.length === 0) { defaultMaxListeners = n; } else { if (isEventTarget === undefined) isEventTarget = __hoisted_internal_event_target__.isEventTarget; for (let i = 0; i < eventTargets.length; i++) { const target = eventTargets[i]; if (isEventTarget(target)) { target[kMaxEventTargetListeners] = n; target[kMaxEventTargetListenersWarned] = false; } else if (typeof target.setMaxListeners === "function") { target.setMaxListeners(n); } else { throw new ERR_INVALID_ARG_TYPE( "eventTargets", ["EventEmitter", "EventTarget"], target, ); } } } }; // If you're updating this function definition, please also update any // re-definitions, such as the one in the Domain module (lib/domain.js). EventEmitter.init = function (opts) { if ( this._events === undefined || this._events === Object.getPrototypeOf(this)._events ) { this._events = { __proto__: null }; this._eventsCount = 0; this[kShapeMode] = false; } else { this[kShapeMode] = true; } this._maxListeners = this._maxListeners || undefined; if (opts?.captureRejections) { validateBoolean(opts.captureRejections, "options.captureRejections"); this[kCapture] = Boolean(opts.captureRejections); } else { // Assigning the kCapture property directly saves an expensive // prototype lookup in a very sensitive hot path. this[kCapture] = EventEmitter.prototype[kCapture]; } }; function addCatch(that, promise, type, args) { if (!that[kCapture]) { return; } // Handle Promises/A+ spec, then could be a getter // that throws on second use. try { const then = promise.then; if (typeof then === "function") { then.call(promise, undefined, function (err) { // The callback is called with nextTick to avoid a follow-up // rejection from this promise. process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args); }); } } catch (err) { that.emit("error", err); } } function emitUnhandledRejectionOrErr(ee, err, type, args) { if (typeof ee[kRejection] === "function") { ee[kRejection](err, type, ...args); } else { // We have to disable the capture rejections mechanism, otherwise // we might end up in an infinite loop. const prev = ee[kCapture]; // If the error handler throws, it is not catchable and it // will end up in 'uncaughtException'. We restore the previous // value of kCapture in case the uncaughtException is present // and the exception is handled. try { ee[kCapture] = false; ee.emit("error", err); } finally { ee[kCapture] = prev; } } } /** * Increases the max listeners of the event emitter. * @param {number} n * @returns {EventEmitter} */ EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { validateNumber(n, "setMaxListeners", 0); this._maxListeners = n; return this; }; function _getMaxListeners(that) { if (that._maxListeners === undefined) return EventEmitter.defaultMaxListeners; return that._maxListeners; } /** * Returns the current max listener value for the event emitter. * @returns {number} */ EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return _getMaxListeners(this); }; function enhanceStackTrace(err, own) { let ctorInfo = ""; try { const { name } = this.constructor; if (name !== "EventEmitter") ctorInfo = ` on ${name} instance`; } catch { // Continue regardless of error. } const sep = `\nEmitted 'error' event${ctorInfo} at:\n`; const errStack = Array.prototype.slice.call( String.prototype.split.call(err.stack, "\n"), 1, ); const ownStack = Array.prototype.slice.call( String.prototype.split.call(own.stack, "\n"), 1, ); const { len, offset } = identicalSequenceRange(ownStack, errStack); if (len > 0) { Array.prototype.splice.call( ownStack, offset + 1, len - 2, " [... lines matching original stack trace ...]", ); } return err.stack + sep + Array.prototype.join.call(ownStack, "\n"); } /** * Synchronously calls each of the listeners registered * for the event. * @param {string | symbol} type * @param {...any} [args] * @returns {boolean} */ EventEmitter.prototype.emit = function emit(type, ...args) { let doError = type === "error"; const events = this._events; if (events !== undefined) { if (doError && events[kErrorMonitor] !== undefined) this.emit(kErrorMonitor, ...args); doError = doError && events.error === undefined; } else if (!doError) return false; // If there is no 'error' event listener then throw. if (doError) { let er; if (args.length > 0) er = args[0]; if (er instanceof Error) { try { const capture = {}; Error.captureStackTrace(capture, EventEmitter.prototype.emit); Object.defineProperty(er, kEnhanceStackBeforeInspector, { __proto__: null, value: Function.prototype.bind.call( enhanceStackTrace, this, er, capture, ), configurable: true, }); } catch { // Continue regardless of error. } // Note: The comments on the `throw` lines are intentional, they show // up in Node's output if this results in an unhandled exception. throw er; // Unhandled 'error' event } let stringifiedEr; try { stringifiedEr = inspect(er); } catch { stringifiedEr = er; } // At least give some kind of context to the user const err = new ERR_UNHANDLED_ERROR(stringifiedEr); err.context = er; throw err; // Unhandled 'error' event } const handler = events[type]; if (handler === undefined) return false; if (typeof handler === "function") { const result = handler.apply(this, args); // We check if result is undefined first because that // is the most common case so we do not pay any perf // penalty if (result !== undefined && result !== null) { addCatch(this, result, type, args); } } else { const len = handler.length; const listeners = arrayClone(handler); for (let i = 0; i < len; ++i) { const result = listeners[i].apply(this, args); // We check if result is undefined first because that // is the most common case so we do not pay any perf // penalty. // This code is duplicated because extracting it away // would make it non-inlineable. if (result !== undefined && result !== null) { addCatch(this, result, type, args); } } } return true; }; function _addListener(target, type, listener, prepend) { let m; let events; let existing; checkListener(listener); events = target._events; if (events === undefined) { events = target._events = { __proto__: null }; target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener !== undefined) { target.emit("newListener", type, listener.listener ?? listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object events = target._events; } existing = events[type]; } if (existing === undefined) { // Optimize the case of one listener. Don't need the extra array object. events[type] = listener; ++target._eventsCount; } else { if (typeof existing === "function") { // Adding the second element, need to change to array. existing = events[type] = prepend ? [listener, existing] : [existing, listener]; // If we've already got an array, just append. } else if (prepend) { existing.unshift(listener); } else { existing.push(listener); } // Check for listener leak m = _getMaxListeners(target); if (m > 0 && existing.length > m && !existing.warned) { existing.warned = true; // No error code for this since it is a Warning const w = genericNodeError( `Possible EventEmitter memory leak detected. ${existing.length} ${String(type)} listeners ` + `added to ${inspect(target, { depth: -1 })}. MaxListeners is ${m}. Use emitter.setMaxListeners() to increase limit`, { name: "MaxListenersExceededWarning", emitter: target, type: type, count: existing.length, }, ); process.emitWarning(w); } } return target; } /** * Adds a listener to the event emitter. * @param {string | symbol} type * @param {Function} listener * @returns {EventEmitter} */ EventEmitter.prototype.addListener = function addListener(type, listener) { return _addListener(this, type, listener, false); }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; /** * Adds the `listener` function to the beginning of * the listeners array. * @param {string | symbol} type * @param {Function} listener * @returns {EventEmitter} */ EventEmitter.prototype.prependListener = function prependListener( type, listener, ) { return _addListener(this, type, listener, true); }; function onceWrapper() { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; if (arguments.length === 0) return this.listener.call(this.target); return this.listener.apply(this.target, arguments); } } function _onceWrap(target, type, listener) { const state = { fired: false, wrapFn: undefined, target, type, listener }; const wrapped = onceWrapper.bind(state); wrapped.listener = listener; state.wrapFn = wrapped; return wrapped; } /** * Adds a one-time `listener` function to the event emitter. * @param {string | symbol} type * @param {Function} listener * @returns {EventEmitter} */ EventEmitter.prototype.once = function once(type, listener) { checkListener(listener); this.on(type, _onceWrap(this, type, listener)); return this; }; /** * Adds a one-time `listener` function to the beginning of * the listeners array. * @param {string | symbol} type * @param {Function} listener * @returns {EventEmitter} */ EventEmitter.prototype.prependOnceListener = function prependOnceListener( type, listener, ) { checkListener(listener); this.prependListener(type, _onceWrap(this, type, listener)); return this; }; /** * Removes the specified `listener` from the listeners array. * @param {string | symbol} type * @param {Function} listener * @returns {EventEmitter} */ EventEmitter.prototype.removeListener = function removeListener( type, listener, ) { checkListener(listener); const events = this._events; if (events === undefined) return this; const list = events[type]; if (list === undefined) return this; if (list === listener || list.listener === listener) { this._eventsCount -= 1; if (this[kShapeMode]) { events[type] = undefined; } else if (this._eventsCount === 0) { this._events = { __proto__: null }; } else { delete events[type]; if (events.removeListener) this.emit("removeListener", type, list.listener || listener); } } else if (typeof list !== "function") { let position = -1; for (let i = list.length - 1; i >= 0; i--) { if (list[i] === listener || list[i].listener === listener) { position = i; break; } } if (position < 0) return this; if (position === 0) list.shift(); else { if (spliceOne === undefined) spliceOne = __hoisted_internal_util__.spliceOne; spliceOne(list, position); } if (list.length === 1) events[type] = list[0]; if (events.removeListener !== undefined) this.emit("removeListener", type, listener); } return this; }; EventEmitter.prototype.off = EventEmitter.prototype.removeListener; /** * Removes all listeners from the event emitter. (Only * removes listeners for a specific event name if specified * as `type`). * @param {string | symbol} [type] * @returns {EventEmitter} */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { const events = this._events; if (events === undefined) return this; // Not listening for removeListener, no need to emit if (events.removeListener === undefined) { if (arguments.length === 0) { this._events = { __proto__: null }; this._eventsCount = 0; } else if (events[type] !== undefined) { if (--this._eventsCount === 0) this._events = { __proto__: null }; else delete events[type]; } this[kShapeMode] = false; return this; } // Emit removeListener for all listeners on all events if (arguments.length === 0) { for (const key of Reflect.ownKeys(events)) { if (key === "removeListener") continue; this.removeAllListeners(key); } this.removeAllListeners("removeListener"); this._events = { __proto__: null }; this._eventsCount = 0; this[kShapeMode] = false; return this; } const listeners = events[type]; if (typeof listeners === "function") { this.removeListener(type, listeners); } else if (listeners !== undefined) { // LIFO order for (let i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]); } } return this; }; function _listeners(target, type, unwrap) { const events = target._events; if (events === undefined) return []; const evlistener = events[type]; if (evlistener === undefined) return []; if (typeof evlistener === "function") return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener); } /** * Returns a copy of the array of listeners for the event name * specified as `type`. * @param {string | symbol} type * @returns {Function[]} */ EventEmitter.prototype.listeners = function listeners(type) { return _listeners(this, type, true); }; /** * Returns a copy of the array of listeners and wrappers for * the event name specified as `type`. * @param {string | symbol} type * @returns {Function[]} */ EventEmitter.prototype.rawListeners = function rawListeners(type) { return _listeners(this, type, false); }; /** * Returns the number of listeners listening to the event name * specified as `type`. * @deprecated since v3.2.0 * @param {EventEmitter} emitter * @param {string | symbol} type * @returns {number} */ EventEmitter.listenerCount = function (emitter, type) { if (typeof emitter.listenerCount === "function") { return emitter.listenerCount(type); } return Function.prototype.call.call(listenerCount, emitter, type); }; EventEmitter.prototype.listenerCount = listenerCount; /** * Returns the number of listeners listening to event name * specified as `type`. * @param {string | symbol} type * @param {Function} listener * @returns {number} */ function listenerCount(type, listener) { const events = this._events; if (events !== undefined) { const evlistener = events[type]; if (typeof evlistener === "function") { if (listener != null) { return listener === evlistener || listener === evlistener.listener ? 1 : 0; } return 1; } else if (evlistener !== undefined) { if (listener != null) { let matching = 0; for (let i = 0, l = evlistener.length; i < l; i++) { if ( evlistener[i] === listener || evlistener[i].listener === listener ) { matching++; } } return matching; } return evlistener.length; } } return 0; } /** * Returns an array listing the events for which * the emitter has registered listeners. * @returns {any[]} */ EventEmitter.prototype.eventNames = function eventNames() { return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; }; function arrayClone(arr) { // At least since V8 8.3, this implementation is faster than the previous // which always used a simple for-loop switch (arr.length) { case 2: return [arr[0], arr[1]]; case 3: return [arr[0], arr[1], arr[2]]; case 4: return [arr[0], arr[1], arr[2], arr[3]]; case 5: return [arr[0], arr[1], arr[2], arr[3], arr[4]]; case 6: return [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]]; } return Array.prototype.slice.call(arr); } function unwrapListeners(arr) { const ret = arrayClone(arr); for (let i = 0; i < ret.length; ++i) { const orig = ret[i].listener; if (typeof orig === "function") ret[i] = orig; } return ret; } /** * Returns a copy of the array of listeners for the event name * specified as `type`. * @param {EventEmitter | EventTarget} emitterOrTarget * @param {string | symbol} type * @returns {Function[]} */ function getEventListeners(emitterOrTarget, type) { // First check if EventEmitter if (typeof emitterOrTarget.listeners === "function") { return emitterOrTarget.listeners(type); } // Require event target lazily to avoid always loading it const { isEventTarget, kEvents } = __hoisted_internal_event_target__; if (isEventTarget(emitterOrTarget)) { const root = emitterOrTarget[kEvents].get(type); const listeners = []; let handler = root?.next; while (handler?.listener !== undefined) { const listener = handler.listener?.deref ? handler.listener.deref() : handler.listener; listeners.push(listener); handler = handler.next; } return listeners; } throw new ERR_INVALID_ARG_TYPE( "emitter", ["EventEmitter", "EventTarget"], emitterOrTarget, ); } /** * Returns the max listeners set. * @param {EventEmitter | EventTarget} emitterOrTarget * @returns {number} */ function getMaxListeners(emitterOrTarget) { if (typeof emitterOrTarget?.getMaxListeners === "function") { return _getMaxListeners(emitterOrTarget); } else if (emitterOrTarget?.[kMaxEventTargetListeners]) { return emitterOrTarget[kMaxEventTargetListeners]; } throw new ERR_INVALID_ARG_TYPE( "emitter", ["EventEmitter", "EventTarget"], emitterOrTarget, ); } /** * Creates a `Promise` that is fulfilled when the emitter * emits the given event. * @param {EventEmitter} emitter * @param {string | symbol} name * @param {{ signal: AbortSignal; }} [options] * @returns {Promise} */ async function once(emitter, name, options = kEmptyObject) { validateObject(options, "options"); const signal = options?.signal; validateAbortSignal(signal, "options.signal"); if (signal?.aborted) throw new AbortError(undefined, { cause: signal?.reason }); return new Promise((resolve, reject) => { const errorListener = (err) => { emitter.removeListener(name, resolver); if (signal != null) { eventTargetAgnosticRemoveListener(signal, "abort", abortListener); } reject(err); }; const resolver = (...args) => { if (typeof emitter.removeListener === "function") { emitter.removeListener("error", errorListener); } if (signal != null) { eventTargetAgnosticRemoveListener(signal, "abort", abortListener); } resolve(args); }; kResistStopPropagation ??= __hoisted_internal_event_target__.kResistStopPropagation; const opts = { __proto__: null, once: true, [kResistStopPropagation]: true, }; eventTargetAgnosticAddListener(emitter, name, resolver, opts); if (name !== "error" && typeof emitter.once === "function") { // EventTarget does not have `error` event semantics like Node // EventEmitters, we listen to `error` events only on EventEmitters. emitter.once("error", errorListener); } function abortListener() { eventTargetAgnosticRemoveListener(emitter, name, resolver); eventTargetAgnosticRemoveListener(emitter, "error", errorListener); reject(new AbortError(undefined, { cause: signal?.reason })); } if (signal != null) { eventTargetAgnosticAddListener(signal, "abort", abortListener, { __proto__: null, once: true, [kResistStopPropagation]: true, }); } }); } const AsyncIteratorPrototype = Object.getPrototypeOf( Object.getPrototypeOf(async function* () {}).prototype, ); function createIterResult(value, done) { return { value, done }; } function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) { if (typeof emitter.removeListener === "function") { emitter.removeListener(name, listener); } else if (typeof emitter.removeEventListener === "function") { emitter.removeEventListener(name, listener, flags); } else { throw new ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter); } } function eventTargetAgnosticAddListener(emitter, name, listener, flags) { if (typeof emitter.on === "function") { if (flags?.once) { emitter.once(name, listener); } else { emitter.on(name, listener); } } else if (typeof emitter.addEventListener === "function") { emitter.addEventListener(name, listener, flags); } else { throw new ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter); } } /** * Returns an `AsyncIterator` that iterates `event` events. * @param {EventEmitter} emitter * @param {string | symbol} event * @param {{ * signal: AbortSignal; * close?: string[]; * highWaterMark?: number, * lowWaterMark?: number * }} [options] * @returns {AsyncIterator} */ function on(emitter, event, options = kEmptyObject) { // Parameters validation validateObject(options, "options"); const signal = options.signal; validateAbortSignal(signal, "options.signal"); if (signal?.aborted) throw new AbortError(undefined, { cause: signal?.reason }); // Support both highWaterMark and highWatermark for backward compatibility const highWatermark = options.highWaterMark ?? options.highWatermark ?? Number.MAX_SAFE_INTEGER; validateInteger(highWatermark, "options.highWaterMark", 1); // Support both lowWaterMark and lowWatermark for backward compatibility const lowWatermark = options.lowWaterMark ?? options.lowWatermark ?? 1; validateInteger(lowWatermark, "options.lowWaterMark", 1); // Preparing controlling queues and variables FixedQueue ??= __hoisted_internal_fixed_queue__; const unconsumedEvents = new FixedQueue(); const unconsumedPromises = new FixedQueue(); let paused = false; let error = null; let finished = false; let size = 0; const iterator = Object.setPrototypeOf( { next() { // First, we consume all unread events if (size) { const value = unconsumedEvents.shift(); size--; if (paused && size < lowWatermark) { emitter.resume(); paused = false; } return Promise.resolve(createIterResult(value, false)); } // Then we error, if an error happened // This happens one time if at all, because after 'error' // we stop listening if (error) { const p = Promise.reject(error); // Only the first element errors error = null; return p; } // If the iterator is finished, resolve to done if (finished) return closeHandler(); // Wait until an event happens return new Promise(function (resolve, reject) { unconsumedPromises.push({ resolve, reject }); }); }, return() { return closeHandler(); }, throw(err) { if (!err || !(err instanceof Error)) { throw new ERR_INVALID_ARG_TYPE( "EventEmitter.AsyncIterator", "Error", err, ); } errorHandler(err); }, [SymbolAsyncIterator]() { return this; }, [kWatermarkData]: { /** * The current queue size */ get size() { return size; }, /** * The low watermark. The emitter is resumed every time size is lower than it */ get low() { return lowWatermark; }, /** * The high watermark. The emitter is paused every time size is higher than it */ get high() { return highWatermark; }, /** * It checks whether the emitter is paused by the watermark controller or not */ get isPaused() { return paused; }, }, }, AsyncIteratorPrototype, ); // Adding event handlers const { addEventListener, removeAll } = listenersController(); kFirstEventParam ??= __hoisted_internal_events_symbols__.kFirstEventParam; addEventListener( emitter, event, options[kFirstEventParam] ? eventHandler : function (...args) { return eventHandler(args); }, ); if (event !== "error" && typeof emitter.on === "function") { addEventListener(emitter, "error", errorHandler); } const closeEvents = options?.close; if (closeEvents?.length) { for (let i = 0; i < closeEvents.length; i++) { addEventListener(emitter, closeEvents[i], closeHandler); } } const abortListenerDisposable = signal ? addAbortListener(signal, abortListener) : null; return iterator; function abortListener() { errorHandler(new AbortError(undefined, { cause: signal?.reason })); } function eventHandler(value) { if (unconsumedPromises.isEmpty()) { size++; if (!paused && size > highWatermark) { paused = true; emitter.pause(); } unconsumedEvents.push(value); } else unconsumedPromises.shift().resolve(createIterResult(value, false)); } function errorHandler(err) { if (unconsumedPromises.isEmpty()) error = err; else unconsumedPromises.shift().reject(err); closeHandler(); } function closeHandler() { abortListenerDisposable?.[Symbol.for("nodejs.dispose")](); removeAll(); finished = true; const doneResult = createIterResult(undefined, true); while (!unconsumedPromises.isEmpty()) { unconsumedPromises.shift().resolve(doneResult); } return Promise.resolve(doneResult); } } function listenersController() { const listeners = []; return { addEventListener(emitter, event, handler, flags) { eventTargetAgnosticAddListener(emitter, event, handler, flags); Array.prototype.push.call(listeners, [emitter, event, handler, flags]); }, removeAll() { while (listeners.length > 0) { ReflectApply( eventTargetAgnosticRemoveListener, undefined, Array.prototype.pop.call(listeners), ); } }, }; } export { EventEmitter }; export { once }; export { on }; export { getEventListeners }; export { getMaxListeners };