Source: event.js

const data = new WeakMap()

/**
 * An instance of this class represents an event that describes some
 * change in state or proposed change in state in the system.
 * @since 0.0.0
 * @example
 * // create a new event
 * const event = new Event({
 *    type: 'add',
 *    payload: {
 *       id: 0,
 *       name: 'my file.txt'
 *       item: someBlob
 *    }
 * })
 */
class Event {
    /**
     * Create a new Event.
     *
     * @param {Object} eventData
     * @param {boolean} [eventData.informational=false] True to mark this event
     * as information. That is, no action can be taken by listeners to affect the
     * event.
     * @param {*} [eventData.payload] - Data associated with this event
     * from the entity that triggered the event.
     * @param {string} eventData.type - Type of event to fire.
     * @since 0.0.0
     */
    constructor({informational = false, payload, type}) {
        data.set(this, {
            informational: informational,
            payload: payload,
            type: type
        })
    }

    /**
     * Marks this event as cancelled. When an event is cancelled, the event
     * will continue to bubble up through the list of registered listeners,
     * but the action associated with the event must no longer occur. For example,
     * if the "add" event is cancelled, the item to be added must not actually be
     * added to the system, but the "add" event will continue to reach all subsequent
     * listeners. Return values from subsequent listeners will be ignored for cancelled
     * events as well.
     *
     * Another use case for cancelling an event - a listener that has already performed
     * the action associated with the event and want to prevent any subsequent listeners
     * from performing any further actions.
     *
     * @since 0.0.0
     */
    cancel() {
        data.get(this).cancelled = true
    }

    /**
     * True if the event was cancelled. Event listeners must short-circuit and not perform
     * any intended action associated with an event if it has been cancelled. Also note
     * that return values for subsequent listeners are ignored once the event has been
     * cancelled.
     *
     * @type {boolean}
     * @since 0.0.0
     * @readonly
     */
    get cancelled() {
        return !!data.get(this).cancelled
    }

    /**
     * True if the event is informational. False if it is actionable.
     *
     * @type {boolean}
     * @since 0.0.0
     * @readonly
     */
    get informational() {
        return data.get(this).informational
    }

    /**
     * Data associated with this event from the entity that triggered the event.
     * Do NOT mutate the value of this property. Instead, create a deep copy
     * and mutate the copy.
     *
     * @type {*}
     * @since 0.0.0
     * @readonly
     */
    get payload() {
        return data.get(this).payload
    }

    set result(newResult) {
        data.get(this).result = newResult
    }

    /**
     * Data provided by the previous event handler. In many, if not most cases,
     * this result value, which matches the return value of the most recent
     * event listener, replaces the [payload]{@link Event#payload} specified
     * by the creator of the event. Lack of a result indicates that the data
     * provided by the event creator is in effect and will be considered by all
     * listeners responding to the event. In other words, listeners that want to
     * affect the data associated with this event must clone the payload, make
     * appropriate changes, and then return this modified copy. Exceptional
     * events where this logic is not followed should be documented.
     *
     * Do NOT mutate the value of this property. Instead, create a deep
     * copy and mutate the copy.
     *
     * @type {(Object|Array|undefined)}
     * @since 0.0.0
     * @readonly
     */
    get result() {
        return data.get(this).result
    }

    /**
     * Type of this event.
     *
     * @since 0.0.0
     * @type {string}
     * @readonly
     */
    get type() {
        return data.get(this).type
    }
}

export default Event