UNPKG

ts-typed-events

Version:

Zero dependency strongly typed event emitters for TypeScript

213 lines (212 loc) 8.1 kB
"use strict"; /** * This is a very simple strongly typed event emitter class, see README.md * for more details. */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); exports.__esModule = true; exports.createEventEmitter = exports.Event = exports.createEmitter = exports.SealedEvent = exports.BaseEvent = void 0; /** * A ts-typed-events event to register callbacks on to be invoked when they are * emitted. */ var BaseEvent = /** @class */ (function () { /** * Marked as protected to discourage creation outside of * createEventEmitter(). */ function BaseEvent() { /** All the current listeners for this event. */ this.listeners = []; // pass } /** * Attaches a callback to trigger on all emits for this event. * * @param callback - The callback to invoke on all emits. */ BaseEvent.prototype.on = function (callback) { this.listeners.push({ once: false, callback: callback }); }; /** * Attaches a callback to trigger on only the first emit for this event. * * This version either takes a callback or returns a promise. * * @param callback - Optional callback, if specified invokes the callback * only once when the event is triggered, then removes it. * Otherwise returns a promise that resolves with the value the next time * this event is triggered. * @returns Nothing if a callback is passed, otherwise a Promise that * should resolve once this Event emits. */ BaseEvent.prototype.once = function (callback) { var _this = this; if (!callback) { // then they want us to return the promise var promise = new Promise(function (resolve) { // this will invoke the version that has a callback, // so resolve can be used as the callback _this.once(resolve); }); // attach the promise we just made to the listener (it was pushed // on the end via this.once() above) this.listeners[this.listeners.length - 1].promise = promise; return promise; } // else we were sent a normal callback, so attach it this.listeners.push({ once: true, callback: callback }); }; /** * Removes a callback from this event (regardless of once vs on). * * Returns true if a callback was removed, false otherwise. * * @param listener - The callback to remove. * @returns True if a callback was removed, false otherwise. */ BaseEvent.prototype.off = function (listener) { var originalLength = this.listeners.length; // remove all listeners that have the same callback as this one this.listeners = this.listeners.filter(function (l) { return listener !== l.callback && (!l.promise || listener !== l.promise); }); return this.listeners.length !== originalLength; }; /** * Removes ALL callbacks from this event, regardless of once vs on. * * Returns the number of listeners removed. * * @returns The number of listeners removed. */ BaseEvent.prototype.offAll = function () { var originalLength = this.listeners.length; this.listeners.length = 0; // empty our listener array return originalLength; }; return BaseEvent; }()); exports.BaseEvent = BaseEvent; /** * A ts-typed-events event to register callbacks on to be invoked when they are * emitted. * * This Event class signifies the event is "sealed" from the outside and cannot * be emitted through itself. The emitter function is separated. */ var SealedEvent = /** @class */ (function (_super) { __extends(SealedEvent, _super); function SealedEvent() { return _super !== null && _super.apply(this, arguments) || this; } return SealedEvent; }(BaseEvent)); exports.SealedEvent = SealedEvent; /** @internal */ function createEmitterWithBaseEvent(event) { // Hack-y, we are reaching into to grab the listeners // realistically, this would be a friend style function var publicListenersEvent = event; /** * The emitter function for the event. * * @param emitting - Whatever is being emitted. * @returns True if any listeners were emitted to, false otherwise. */ function emit(emitting) { var listeners = publicListenersEvent.listeners; var hadListeners = listeners.length > 0; for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) { var listener = listeners_1[_i]; listener.callback(emitting); } // remove all listeners that only wanted to listen once publicListenersEvent.listeners = listeners.filter(function (_a) { var once = _a.once; return !once; }); return hadListeners; } // Hack: Need to find a better way to convince TS this function will have // the correct keys on these circular functions. /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */ var func = emit; func.event = event; func.emit = emit; var emitter = func; /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */ return emitter; } /** * Creates and returns an emitter for a SealedEvent, with the event keyed off * the emitter via `.event`. * * @returns An emitter function that will emit to the `.event` SealedEvent. */ function createEmitter() { // This is a hack-y way to create a new class instance that doesn't want you // to. var EventClass = SealedEvent; return createEmitterWithBaseEvent(new EventClass()); } exports.createEmitter = createEmitter; /** * A specialized Event that holds a reference to its own emit function. * This allows any code with access to the Event to also trigger emits. */ var Event = /** @class */ (function (_super) { __extends(Event, _super); /** * Creates a new Event, with its emit accessible as a member function. */ function Event() { var _this = _super.call(this) || this; /** * Emits a value to all the listeners, triggering their callbacks. * Returns true if the event had listeners emitted to, * false otherwise. * Because this exists on the event, any code with access to this event * can trigger the callback for all listeners. * * @param emitting - If the Event has a type, this is the data of that type * to emit to all listeners. If no type (undefined) this argument should * be omitted. * @returns True if the event had listeners emitted to, false otherwise. */ _this.emit = createEmitterWithBaseEvent(_this); return _this; } return Event; }(BaseEvent)); exports.Event = Event; /** * Creates and returns an emitter for an Event, with the event keyed off * the emitter via `.event`. * **Note**: The `event` here is will have a member function `.emit` that emits * to the same function as the emitter returned here. * * @returns An emitter function that will emit to the `.event` Event. */ function createEventEmitter() { return createEmitterWithBaseEvent(new Event()); } exports.createEventEmitter = createEventEmitter;