code: function callListenersSequentially(i, args) {
      if (!this.events[eventName][i]) {
        // If there is no more listener to execute, we return a promise of the event arguments/result
        // So we are getting out of the recursive function
        return new Promise((resolve, reject) => {
          resolve(args);
        });
      }
  
      const p = Promise.resolve(this.events[eventName][i].apply(this.events[eventName][i], args));
      return p.then(function propagateArguments() {
        // We cannot use the "=>" notation here because we use `"arguments"
        // When an event listener has been executed, we move to the next one (recursivity)
        if (propagateResponses) {
          // Case the event must pass the result of a listener to the next one
          // This use case allow to transmit and alter primitive (strings, numbers ...)
          return callListenersSequentially.bind(this)(i + 1, arguments[0]);
        }
        // Case the event can pass the arguments to each listener
        // This use case allow a simple syntax for listener, compatible with fireConcurrently()
        // But it is not possible to transmit alterations made on primitive arguments
        return callListenersSequentially.bind(this)(i + 1, args);
      }.bind(this));
    }
docs: Define a recusive function that will check if a plugin implements the hook, execute it and pass the eventually transformed arguments to the next one