import { ERR_EVENT_RECURSION, ERR_INVALID_ARG_TYPE, ERR_INVALID_THIS, ERR_MISSING_ARGS,} from "./errors.ts";import { validateObject, validateString } from "./validators.mjs";import { emitWarning } from "../process.ts";import { nextTick } from "../_next_tick.ts";
import { customInspectSymbol, kEmptyObject, kEnumerableProperty,} from "./util.mjs";import { inspect } from "../util.ts";
const kIsEventTarget = Symbol.for("nodejs.event_target");const kIsNodeEventTarget = Symbol("kIsNodeEventTarget");
import { EventEmitter } from "../events.ts";const { kMaxEventTargetListeners, kMaxEventTargetListenersWarned,} = EventEmitter;
const kEvents = Symbol("kEvents");const kIsBeingDispatched = Symbol("kIsBeingDispatched");const kStop = Symbol("kStop");const kTarget = Symbol("kTarget");const kHandlers = Symbol("khandlers");const kWeakHandler = Symbol("kWeak");
const kHybridDispatch = Symbol.for("nodejs.internal.kHybridDispatch");const kCreateEvent = Symbol("kCreateEvent");const kNewListener = Symbol("kNewListener");const kRemoveListener = Symbol("kRemoveListener");const kIsNodeStyleListener = Symbol("kIsNodeStyleListener");const kTrustEvent = Symbol("kTrustEvent");
const kType = Symbol("type");const kDetail = Symbol("detail");const kDefaultPrevented = Symbol("defaultPrevented");const kCancelable = Symbol("cancelable");const kTimestamp = Symbol("timestamp");const kBubbles = Symbol("bubbles");const kComposed = Symbol("composed");const kPropagationStopped = Symbol("propagationStopped");
function isEvent(value) { return typeof value?.[kType] === "string";}
class Event extends globalThis.Event { constructor(type, options = null) { super(type, options); if (arguments.length === 0) { throw new ERR_MISSING_ARGS("type"); } validateObject(options, "options", { allowArray: true, allowFunction: true, nullable: true, }); const { cancelable, bubbles, composed } = { ...options }; this[kCancelable] = !!cancelable; this[kBubbles] = !!bubbles; this[kComposed] = !!composed; this[kType] = `${type}`; this[kDefaultPrevented] = false; this[kTimestamp] = performance.now(); this[kPropagationStopped] = false; this[kTarget] = null; this[kIsBeingDispatched] = false; }
[customInspectSymbol](depth, options) { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } const name = this.constructor.name; if (depth < 0) { return name; }
const opts = Object.assign({}, options, { depth: NumberIsInteger(options.depth) ? options.depth - 1 : options.depth, });
return `${name} ${ inspect({ type: this[kType], defaultPrevented: this[kDefaultPrevented], cancelable: this[kCancelable], timeStamp: this[kTimestamp], }, opts) }`; }
stopImmediatePropagation() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } this[kStop] = true; }
preventDefault() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } this[kDefaultPrevented] = true; }
get target() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kTarget]; }
get currentTarget() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kTarget]; }
get srcElement() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kTarget]; }
get type() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kType]; }
get cancelable() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kCancelable]; }
get defaultPrevented() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kCancelable] && this[kDefaultPrevented]; }
get timeStamp() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kTimestamp]; }
composedPath() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kIsBeingDispatched] ? [this[kTarget]] : []; }
get returnValue() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return !this.defaultPrevented; }
get bubbles() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kBubbles]; }
get composed() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kComposed]; }
get eventPhase() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kIsBeingDispatched] ? Event.AT_TARGET : Event.NONE; }
get cancelBubble() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } return this[kPropagationStopped]; }
set cancelBubble(value) { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } if (value) { this.stopPropagation(); } }
stopPropagation() { if (!isEvent(this)) { throw new ERR_INVALID_THIS("Event"); } this[kPropagationStopped] = true; }
static NONE = 0; static CAPTURING_PHASE = 1; static AT_TARGET = 2; static BUBBLING_PHASE = 3;}
Object.defineProperties( Event.prototype, { [Symbol.toStringTag]: { writable: true, enumerable: false, configurable: true, value: "Event", }, stopImmediatePropagation: kEnumerableProperty, preventDefault: kEnumerableProperty, target: kEnumerableProperty, currentTarget: kEnumerableProperty, srcElement: kEnumerableProperty, type: kEnumerableProperty, cancelable: kEnumerableProperty, defaultPrevented: kEnumerableProperty, timeStamp: kEnumerableProperty, composedPath: kEnumerableProperty, returnValue: kEnumerableProperty, bubbles: kEnumerableProperty, composed: kEnumerableProperty, eventPhase: kEnumerableProperty, cancelBubble: kEnumerableProperty, stopPropagation: kEnumerableProperty, },);
function isCustomEvent(value) { return isEvent(value) && (value?.[kDetail] !== undefined);}
class CustomEvent extends Event { constructor(type, options = kEmptyObject) { if (arguments.length === 0) { throw new ERR_MISSING_ARGS("type"); } super(type, options); this[kDetail] = options?.detail ?? null; }
get detail() { if (!isCustomEvent(this)) { throw new ERR_INVALID_THIS("CustomEvent"); } return this[kDetail]; }}
Object.defineProperties(CustomEvent.prototype, { [Symbol.toStringTag]: { __proto__: null, writable: false, enumerable: false, configurable: true, value: "CustomEvent", }, detail: kEnumerableProperty,});
class NodeCustomEvent extends Event { constructor(type, options) { super(type, options); if (options?.detail) { this.detail = options.detail; } }}
let weakListenersState = null;let objectToWeakListenerMap = null;function weakListeners() { weakListenersState ??= new FinalizationRegistry( (listener) => listener.remove(), ); objectToWeakListenerMap ??= new WeakMap(); return { registry: weakListenersState, map: objectToWeakListenerMap };}
class Listener { constructor( previous, listener, once, capture, passive, isNodeStyleListener, weak, ) { this.next = undefined; if (previous !== undefined) { previous.next = this; } this.previous = previous; this.listener = listener; this.once = once; this.capture = capture; this.passive = passive; this.isNodeStyleListener = isNodeStyleListener; this.removed = false; this.weak = Boolean(weak);
if (this.weak) { this.callback = new WeakRef(listener); weakListeners().registry.register(listener, this, this); weakListeners().map.set(weak, listener); this.listener = this.callback; } else if (typeof listener === "function") { this.callback = listener; this.listener = listener; } else { this.callback = Function.prototype.bind.call( listener.handleEvent, listener, ); this.listener = listener; } }
same(listener, capture) { const myListener = this.weak ? this.listener.deref() : this.listener; return myListener === listener && this.capture === capture; }
remove() { if (this.previous !== undefined) { this.previous.next = this.next; } if (this.next !== undefined) { this.next.previous = this.previous; } this.removed = true; if (this.weak) { weakListeners().registry.unregister(this); } }}
function initEventTarget(self) { self[kEvents] = new Map(); self[kMaxEventTargetListeners] = EventEmitter.defaultMaxListeners; self[kMaxEventTargetListenersWarned] = false;}
class EventTarget extends globalThis.EventTarget { static [kIsEventTarget] = true;
constructor() { super(); initEventTarget(this); }
[kNewListener](size, type, _listener, _once, _capture, _passive, _weak) { if ( this[kMaxEventTargetListeners] > 0 && size > this[kMaxEventTargetListeners] && !this[kMaxEventTargetListenersWarned] ) { this[kMaxEventTargetListenersWarned] = true; const w = new Error( "Possible EventTarget memory leak detected. " + `${size} ${type} listeners ` + `added to ${inspect(this, { depth: -1 })}. Use ` + "events.setMaxListeners() to increase limit", ); w.name = "MaxListenersExceededWarning"; w.target = this; w.type = type; w.count = size; emitWarning(w); } } [kRemoveListener](_size, _type, _listener, _capture) {}
addEventListener(type, listener, options = {}) { if (!isEventTarget(this)) { throw new ERR_INVALID_THIS("EventTarget"); } if (arguments.length < 2) { throw new ERR_MISSING_ARGS("type", "listener"); }
const { once, capture, passive, signal, isNodeStyleListener, weak, } = validateEventListenerOptions(options);
if (!shouldAddListener(listener)) { const w = new Error( `addEventListener called with ${listener}` + " which has no effect.", ); w.name = "AddEventListenerArgumentTypeWarning"; w.target = this; w.type = type; emitWarning(w); return; } type = String(type);
if (signal) { if (signal.aborted) { return; } signal.addEventListener("abort", () => { this.removeEventListener(type, listener, options); }, { once: true, [kWeakHandler]: this }); }
let root = this[kEvents].get(type);
if (root === undefined) { root = { size: 1, next: undefined }; new Listener( root, listener, once, capture, passive, isNodeStyleListener, weak, ); this[kNewListener]( root.size, type, listener, once, capture, passive, weak, ); this[kEvents].set(type, root); return; }
let handler = root.next; let previous = root;
while (handler !== undefined && !handler.same(listener, capture)) { previous = handler; handler = handler.next; }
if (handler !== undefined) { return; }
new Listener( previous, listener, once, capture, passive, isNodeStyleListener, weak, ); root.size++; this[kNewListener](root.size, type, listener, once, capture, passive, weak); }
removeEventListener(type, listener, options = {}) { if (!isEventTarget(this)) { throw new ERR_INVALID_THIS("EventTarget"); } if (!shouldAddListener(listener)) { return; }
type = String(type); const capture = options?.capture === true;
const root = this[kEvents].get(type); if (root === undefined || root.next === undefined) { return; }
let handler = root.next; while (handler !== undefined) { if (handler.same(listener, capture)) { handler.remove(); root.size--; if (root.size === 0) { this[kEvents].delete(type); } this[kRemoveListener](root.size, type, listener, capture); break; } handler = handler.next; } }
dispatchEvent(event) { if (!isEventTarget(this)) { throw new ERR_INVALID_THIS("EventTarget"); }
if (!(event instanceof globalThis.Event)) { throw new ERR_INVALID_ARG_TYPE("event", "Event", event); }
if (event[kIsBeingDispatched]) { throw new ERR_EVENT_RECURSION(event.type); }
this[kHybridDispatch](event, event.type, event);
return event.defaultPrevented !== true; }
[kHybridDispatch](nodeValue, type, event) { const createEvent = () => { if (event === undefined) { event = this[kCreateEvent](nodeValue, type); event[kTarget] = this; event[kIsBeingDispatched] = true; } return event; }; if (event !== undefined) { event[kTarget] = this; event[kIsBeingDispatched] = true; }
const root = this[kEvents].get(type); if (root === undefined || root.next === undefined) { if (event !== undefined) { event[kIsBeingDispatched] = false; } return true; }
let handler = root.next; let next;
while ( handler !== undefined && (handler.passive || event?.[kStop] !== true) ) { next = handler.next;
if (handler.removed) { handler = next; continue; } if (handler.once) { handler.remove(); root.size--; const { listener, capture } = handler; this[kRemoveListener](root.size, type, listener, capture); }
try { let arg; if (handler.isNodeStyleListener) { arg = nodeValue; } else { arg = createEvent(); } const callback = handler.weak ? handler.callback.deref() : handler.callback; let result; if (callback) { result = callback.call(this, arg); if (!handler.isNodeStyleListener) { arg[kIsBeingDispatched] = false; } } if (result !== undefined && result !== null) { addCatch(result); } } catch (err) { emitUncaughtException(err); }
handler = next; }
if (event !== undefined) { event[kIsBeingDispatched] = false; } }
[kCreateEvent](nodeValue, type) { return new NodeCustomEvent(type, { detail: nodeValue }); } [customInspectSymbol](depth, options) { if (!isEventTarget(this)) { throw new ERR_INVALID_THIS("EventTarget"); } const name = this.constructor.name; if (depth < 0) { return name; }
const opts = ObjectAssign({}, options, { depth: Number.isInteger(options.depth) ? options.depth - 1 : options.depth, });
return `${name} ${inspect({}, opts)}`; }}
Object.defineProperties(EventTarget.prototype, { addEventListener: kEnumerableProperty, removeEventListener: kEnumerableProperty, dispatchEvent: kEnumerableProperty, [Symbol.toStringTag]: { writable: true, enumerable: false, configurable: true, value: "EventTarget", },});
function initNodeEventTarget(self) { initEventTarget(self);}
class NodeEventTarget extends EventTarget { static [kIsNodeEventTarget] = true; static defaultMaxListeners = 10;
constructor() { super(); initNodeEventTarget(this); }
setMaxListeners(n) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } EventEmitter.setMaxListeners(n, this); }
getMaxListeners() { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } return this[kMaxEventTargetListeners]; }
eventNames() { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } return Array.from(this[kEvents].keys()); }
listenerCount(type) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } const root = this[kEvents].get(String(type)); return root !== undefined ? root.size : 0; }
off(type, listener, options) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } this.removeEventListener(type, listener, options); return this; }
removeListener(type, listener, options) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } this.removeEventListener(type, listener, options); return this; }
on(type, listener) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); return this; }
addListener(type, listener) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); return this; }
emit(type, arg) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } validateString(type, "type"); const hadListeners = this.listenerCount(type) > 0; this[kHybridDispatch](arg, type); return hadListeners; }
once(type, listener) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } this.addEventListener(type, listener, { once: true, [kIsNodeStyleListener]: true, }); return this; }
removeAllListeners(type) { if (!isNodeEventTarget(this)) { throw new ERR_INVALID_THIS("NodeEventTarget"); } if (type !== undefined) { this[kEvents].delete(String(type)); } else { this[kEvents].clear(); }
return this; }}
Object.defineProperties(NodeEventTarget.prototype, { setMaxListeners: kEnumerableProperty, getMaxListeners: kEnumerableProperty, eventNames: kEnumerableProperty, listenerCount: kEnumerableProperty, off: kEnumerableProperty, removeListener: kEnumerableProperty, on: kEnumerableProperty, addListener: kEnumerableProperty, once: kEnumerableProperty, emit: kEnumerableProperty, removeAllListeners: kEnumerableProperty,});
function shouldAddListener(listener) { if ( typeof listener === "function" || typeof listener?.handleEvent === "function" ) { return true; }
if (listener == null) { return false; }
throw new ERR_INVALID_ARG_TYPE("listener", "EventListener", listener);}
function validateEventListenerOptions(options) { if (typeof options === "boolean") { return { capture: options }; }
if (options === null) { return {}; } validateObject(options, "options", { allowArray: true, allowFunction: true, }); return { once: Boolean(options.once), capture: Boolean(options.capture), passive: Boolean(options.passive), signal: options.signal, weak: options[kWeakHandler], isNodeStyleListener: Boolean(options[kIsNodeStyleListener]), };}
function isEventTarget(obj) { return obj instanceof globalThis.EventTarget;}
function isNodeEventTarget(obj) { return obj?.constructor?.[kIsNodeEventTarget];}
function addCatch(promise) { const then = promise.then; if (typeof then === "function") { then.call(promise, undefined, function (err) { emitUncaughtException(err); }); }}
function emitUncaughtException(err) { nextTick(() => { throw err; });}
function makeEventHandler(handler) { function eventHandler(...args) { if (typeof eventHandler.handler !== "function") { return; } return Reflect.apply(eventHandler.handler, this, args); } eventHandler.handler = handler; return eventHandler;}
function defineEventHandler(emitter, name) { Object.defineProperty(emitter, `on${name}`, { get() { return this[kHandlers]?.get(name)?.handler ?? null; }, set(value) { if (!this[kHandlers]) { this[kHandlers] = new Map(); } let wrappedHandler = this[kHandlers]?.get(name); if (wrappedHandler) { if (typeof wrappedHandler.handler === "function") { this[kEvents].get(name).size--; const size = this[kEvents].get(name).size; this[kRemoveListener](size, name, wrappedHandler.handler, false); } wrappedHandler.handler = value; if (typeof wrappedHandler.handler === "function") { this[kEvents].get(name).size++; const size = this[kEvents].get(name).size; this[kNewListener](size, name, value, false, false, false, false); } } else { wrappedHandler = makeEventHandler(value); this.addEventListener(name, wrappedHandler); } this[kHandlers].set(name, wrappedHandler); }, configurable: true, enumerable: true, });}
const EventEmitterMixin = (Superclass) => { class MixedEventEmitter extends Superclass { constructor(...args) { super(...args); EventEmitter.call(this); } } const protoProps = Object.getOwnPropertyDescriptors(EventEmitter.prototype); delete protoProps.constructor; Object.defineProperties(MixedEventEmitter.prototype, protoProps); return MixedEventEmitter;};
export { CustomEvent, defineEventHandler, Event, EventEmitterMixin, EventTarget, initEventTarget, initNodeEventTarget, isEventTarget, kCreateEvent, kEvents, kNewListener, kRemoveListener, kTrustEvent, kWeakHandler, NodeEventTarget,};
export default { CustomEvent, Event, EventEmitterMixin, EventTarget, NodeEventTarget, defineEventHandler, initEventTarget, initNodeEventTarget, kCreateEvent, kNewListener, kTrustEvent, kRemoveListener, kEvents, kWeakHandler, isEventTarget,};