code:
// 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.

'use strict';

var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
  ? R.apply
  : function ReflectApply(target, receiver, args) {
    return Function.prototype.apply.call(target, receiver, args);
  }

var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
  ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
  ReflectOwnKeys = function ReflectOwnKeys(target) {
    return Object.getOwnPropertyNames(target)
      .concat(Object.getOwnPropertySymbols(target));
  };
} else {
  ReflectOwnKeys = function ReflectOwnKeys(target) {
    return Object.getOwnPropertyNames(target);
  };
}

function ProcessEmitWarning(warning) {
  if (console && console.warn) console.warn(warning);
}

var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
  return value !== value;
}

function EventEmitter() {
  EventEmitter.init.call(this);
}
module.exports = EventEmitter;
module.exports.once = once;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

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.
var defaultMaxListeners = 10;

function checkListener(listener) {
  if (typeof listener !== 'function') {
    throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
  }
}

Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
  enumerable: true,
  get: function() {
    return defaultMaxListeners;
  },
  set: function(arg) {
    if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
      throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
    }
    defaultMaxListeners = arg;
  }
});

EventEmitter.init = function() {

  if (this._events === undefined ||
      this._events === Object.getPrototypeOf(this)._events) {
    this._events = Object.create(null);
    this._eventsCount = 0;
  }

  this._maxListeners = this._maxListeners || undefined;
};

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
  if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
    throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
  }
  this._maxListeners = n;
  return this;
};

function _getMaxListeners(that) {
  if (that._maxListeners === undefined)
    return EventEmitter.defaultMaxListeners;
  return that._maxListeners;
}

EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
  return _getMaxListeners(this);
};

EventEmitter.prototype.emit = function emit(type) {
  var args = [];
  for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
  var doError = (type === 'error');

  var events = this._events;
  if (events !== undefined)
    doError = (doError && events.error === undefined);
  else if (!doError)
    return false;

  // If there is no 'error' event listener then throw.
  if (doError) {
    var er;
    if (args.length > 0)
      er = args[0];
    if (er instanceof 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
    }
    // At least give some kind of context to the user
    var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
    err.context = er;
    throw err; // Unhandled 'error' event
  }

  var handler = events[type];

  if (handler === undefined)
    return false;

  if (typeof handler === 'function') {
    ReflectApply(handler, this, args);
  } else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      ReflectApply(listeners[i], this, args);
  }

  return true;
};

function _addListener(target, type, listener, prepend) {
  var m;
  var events;
  var existing;

  checkListener(listener);

  events = target._events;
  if (events === undefined) {
    events = target._events = Object.create(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.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.
    existing = 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
      // eslint-disable-next-line no-restricted-syntax
      var w = new Error('Possible EventEmitter memory leak detected. ' +
                          existing.length + ' ' + String(type) + ' listeners ' +
                          'added. Use emitter.setMaxListeners() to ' +
                          'increase limit');
      w.name = 'MaxListenersExceededWarning';
      w.emitter = target;
      w.type = type;
      w.count = existing.length;
      ProcessEmitWarning(w);
    }
  }

  return target;
}

EventEmitter.prototype.addListener = function addListener(type, listener) {
  return _addListener(this, type, listener, false);
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

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) {
  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
  var wrapped = onceWrapper.bind(state);
  wrapped.listener = listener;
  state.wrapFn = wrapped;
  return wrapped;
}

EventEmitter.prototype.once = function once(type, listener) {
  checkListener(listener);
  this.on(type, _onceWrap(this, type, listener));
  return this;
};

EventEmitter.prototype.prependOnceListener =
    function prependOnceListener(type, listener) {
      checkListener(listener);
      this.prependListener(type, _onceWrap(this, type, listener));
      return this;
    };

// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
    function removeListener(type, listener) {
      var list, events, position, i, originalListener;

      checkListener(listener);

      events = this._events;
      if (events === undefined)
        return this;

      list = events[type];
      if (list === undefined)
        return this;

      if (list === listener || list.listener === listener) {
        if (--this._eventsCount === 0)
          this._events = Object.create(null);
        else {
          delete events[type];
          if (events.removeListener)
            this.emit('removeListener', type, list.listener || listener);
        }
      } else if (typeof list !== 'function') {
        position = -1;

        for (i = list.length - 1; i >= 0; i--) {
          if (list[i] === listener || list[i].listener === listener) {
            originalListener = list[i].listener;
            position = i;
            break;
          }
        }

        if (position < 0)
          return this;

        if (position === 0)
          list.shift();
        else {
          spliceOne(list, position);
        }

        if (list.length === 1)
          events[type] = list[0];

        if (events.removeListener !== undefined)
          this.emit('removeListener', type, originalListener || listener);
      }

      return this;
    };

EventEmitter.prototype.off = EventEmitter.prototype.removeListener;

EventEmitter.prototype.removeAllListeners =
    function removeAllListeners(type) {
      var listeners, events, i;

      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 = Object.create(null);
          this._eventsCount = 0;
        } else if (events[type] !== undefined) {
          if (--this._eventsCount === 0)
            this._events = Object.create(null);
          else
            delete events[type];
        }
        return this;
      }

      // emit removeListener for all listeners on all events
      if (arguments.length === 0) {
        var keys = Object.keys(events);
        var key;
        for (i = 0; i < keys.length; ++i) {
          key = keys[i];
          if (key === 'removeListener') continue;
          this.removeAllListeners(key);
        }
        this.removeAllListeners('removeListener');
        this._events = Object.create(null);
        this._eventsCount = 0;
        return this;
      }

      listeners = events[type];

      if (typeof listeners === 'function') {
        this.removeListener(type, listeners);
      } else if (listeners !== undefined) {
        // LIFO order
        for (i = listeners.length - 1; i >= 0; i--) {
          this.removeListener(type, listeners[i]);
        }
      }

      return this;
    };

function _listeners(target, type, unwrap) {
  var events = target._events;

  if (events === undefined)
    return [];

  var evlistener = events[type];
  if (evlistener === undefined)
    return [];

  if (typeof evlistener === 'function')
    return unwrap ? [evlistener.listener || evlistener] : [evlistener];

  return unwrap ?
    unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}

EventEmitter.prototype.listeners = function listeners(type) {
  return _listeners(this, type, true);
};

EventEmitter.prototype.rawListeners = function rawListeners(type) {
  return _listeners(this, type, false);
};

EventEmitter.listenerCount = function(emitter, type) {
  if (typeof emitter.listenerCount === 'function') {
    return emitter.listenerCount(type);
  } else {
    return listenerCount.call(emitter, type);
  }
};

EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
  var events = this._events;

  if (events !== undefined) {
    var evlistener = events[type];

    if (typeof evlistener === 'function') {
      return 1;
    } else if (evlistener !== undefined) {
      return evlistener.length;
    }
  }

  return 0;
}

EventEmitter.prototype.eventNames = function eventNames() {
  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};

function arrayClone(arr, n) {
  var copy = new Array(n);
  for (var i = 0; i < n; ++i)
    copy[i] = arr[i];
  return copy;
}

function spliceOne(list, index) {
  for (; index + 1 < list.length; index++)
    list[index] = list[index + 1];
  list.pop();
}

function unwrapListeners(arr) {
  var ret = new Array(arr.length);
  for (var i = 0; i < ret.length; ++i) {
    ret[i] = arr[i].listener || arr[i];
  }
  return ret;
}

function once(emitter, name) {
  return new Promise(function (resolve, reject) {
    function errorListener(err) {
      emitter.removeListener(name, resolver);
      reject(err);
    }

    function resolver() {
      if (typeof emitter.removeListener === 'function') {
        emitter.removeListener('error', errorListener);
      }
      resolve([].slice.call(arguments));
    };

    eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
    if (name !== 'error') {
      addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
    }
  });
}

function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
  if (typeof emitter.on === 'function') {
    eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
  }
}

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') {
    // EventTarget does not have `error` event semantics like Node
    // EventEmitters, we do not listen for `error` events here.
    emitter.addEventListener(name, function wrapListener(arg) {
      // IE does not have builtin `{ once: true }` support so we
      // have to do it manually.
      if (flags.once) {
        emitter.removeEventListener(name, wrapListener);
      }
      listener(arg);
    });
  } else {
    throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
  }
}


events

description: Node's event emitter for all engines.

keywords: events eventEmitter eventDispatcher listeners

author: Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)

type: git

test: node tests/index.js

test:browsers: airtap -- tests/index.js

license: MIT

license: mit


code:
# Security Policy

## Supported Versions
Only the latest major version is supported at any given time.

## Reporting a Vulnerability

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.


code:
// 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.

var common = require('./common');
var assert = require('assert');
var EventEmitter = require('../');

{
  var ee = new EventEmitter();
  var events_new_listener_emitted = [];
  var listeners_new_listener_emitted = [];

  // Sanity check
  assert.strictEqual(ee.addListener, ee.on);

  ee.on('newListener', function(event, listener) {
    // Don't track newListener listeners.
    if (event === 'newListener')
      return;

    events_new_listener_emitted.push(event);
    listeners_new_listener_emitted.push(listener);
  });

  var hello = common.mustCall(function(a, b) {
    assert.strictEqual('a', a);
    assert.strictEqual('b', b);
  });

  ee.once('newListener', function(name, listener) {
    assert.strictEqual(name, 'hello');
    assert.strictEqual(listener, hello);

    var listeners = this.listeners('hello');
    assert.ok(Array.isArray(listeners));
    assert.strictEqual(listeners.length, 0);
  });

  ee.on('hello', hello);
  ee.once('foo', assert.fail);

  assert.ok(Array.isArray(events_new_listener_emitted));
  assert.strictEqual(events_new_listener_emitted.length, 2);
  assert.strictEqual(events_new_listener_emitted[0], 'hello');
  assert.strictEqual(events_new_listener_emitted[1], 'foo');

  assert.ok(Array.isArray(listeners_new_listener_emitted));
  assert.strictEqual(listeners_new_listener_emitted.length, 2);
  assert.strictEqual(listeners_new_listener_emitted[0], hello);
  assert.strictEqual(listeners_new_listener_emitted[1], assert.fail);

  ee.emit('hello', 'a', 'b');
}

// just make sure that this doesn't throw:
{
  var f = new EventEmitter();

  f.setMaxListeners(0);
}

{
  var listen1 = function() {};
  var listen2 = function() {};
  var ee = new EventEmitter();

  ee.once('newListener', function() {
    var listeners = ee.listeners('hello');
    assert.ok(Array.isArray(listeners));
    assert.strictEqual(listeners.length, 0);
    ee.once('newListener', function() {
      var listeners = ee.listeners('hello');
      assert.ok(Array.isArray(listeners));
      assert.strictEqual(listeners.length, 0);
    });
    ee.on('hello', listen2);
  });
  ee.on('hello', listen1);
  // The order of listeners on an event is not always the order in which the
  // listeners were added.
  var listeners = ee.listeners('hello');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 2);
  assert.strictEqual(listeners[0], listen2);
  assert.strictEqual(listeners[1], listen1);
}

// Verify that the listener must be a function
assert.throws(function() {
  var ee = new EventEmitter();

  ee.on('foo', null);
}, /^TypeError: The "listener" argument must be of type Function. Received type object$/);


code:
// 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.

var common = require('./common');
var assert = require('assert');
var events = require('../');

// Redirect warning output to tape.
var consoleWarn = console.warn;
console.warn = common.test.comment;

common.test.on('end', function () {
  console.warn = consoleWarn;
});

// default
{
  var e = new events.EventEmitter();

  for (var i = 0; i < 10; i++) {
    e.on('default', common.mustNotCall());
  }
  assert.ok(!e._events['default'].hasOwnProperty('warned'));
  e.on('default', common.mustNotCall());
  assert.ok(e._events['default'].warned);

  // specific
  e.setMaxListeners(5);
  for (var i = 0; i < 5; i++) {
    e.on('specific', common.mustNotCall());
  }
  assert.ok(!e._events['specific'].hasOwnProperty('warned'));
  e.on('specific', common.mustNotCall());
  assert.ok(e._events['specific'].warned);

  // only one
  e.setMaxListeners(1);
  e.on('only one', common.mustNotCall());
  assert.ok(!e._events['only one'].hasOwnProperty('warned'));
  e.on('only one', common.mustNotCall());
  assert.ok(e._events['only one'].hasOwnProperty('warned'));

  // unlimited
  e.setMaxListeners(0);
  for (var i = 0; i < 1000; i++) {
    e.on('unlimited', common.mustNotCall());
  }
  assert.ok(!e._events['unlimited'].hasOwnProperty('warned'));
}

// process-wide
{
  events.EventEmitter.defaultMaxListeners = 42;
  var e = new events.EventEmitter();

  for (var i = 0; i < 42; ++i) {
    e.on('fortytwo', common.mustNotCall());
  }
  assert.ok(!e._events['fortytwo'].hasOwnProperty('warned'));
  e.on('fortytwo', common.mustNotCall());
  assert.ok(e._events['fortytwo'].hasOwnProperty('warned'));
  delete e._events['fortytwo'].warned;

  events.EventEmitter.defaultMaxListeners = 44;
  e.on('fortytwo', common.mustNotCall());
  assert.ok(!e._events['fortytwo'].hasOwnProperty('warned'));
  e.on('fortytwo', common.mustNotCall());
  assert.ok(e._events['fortytwo'].hasOwnProperty('warned'));
}

// but _maxListeners still has precedence over defaultMaxListeners
{
  events.EventEmitter.defaultMaxListeners = 42;
  var e = new events.EventEmitter();
  e.setMaxListeners(1);
  e.on('uno', common.mustNotCall());
  assert.ok(!e._events['uno'].hasOwnProperty('warned'));
  e.on('uno', common.mustNotCall());
  assert.ok(e._events['uno'].hasOwnProperty('warned'));

  // chainable
  assert.strictEqual(e, e.setMaxListeners(1));
}


code:
// 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.

var test = require('tape');
var assert = require('assert');

var noop = function() {};

var mustCallChecks = [];

function runCallChecks(exitCode) {
  if (exitCode !== 0) return;

  var failed = filter(mustCallChecks, function(context) {
    if ('minimum' in context) {
      context.messageSegment = 'at least ' + context.minimum;
      return context.actual < context.minimum;
    } else {
      context.messageSegment = 'exactly ' + context.exact;
      return context.actual !== context.exact;
    }
  });

  for (var i = 0; i < failed.length; i++) {
    var context = failed[i];
    console.log('Mismatched %s function calls. Expected %s, actual %d.',
        context.name,
        context.messageSegment,
        context.actual);
    // IE8 has no .stack
    if (context.stack) console.log(context.stack.split('\n').slice(2).join('\n'));
  }

  assert.strictEqual(failed.length, 0);
}

exports.mustCall = function(fn, exact) {
  return _mustCallInner(fn, exact, 'exact');
};

function _mustCallInner(fn, criteria, field) {
  if (typeof criteria == 'undefined') criteria = 1;

  if (typeof fn === 'number') {
    criteria = fn;
    fn = noop;
  } else if (fn === undefined) {
    fn = noop;
  }

  if (typeof criteria !== 'number')
    throw new TypeError('Invalid ' + field + ' value: ' + criteria);

  var context = {
    actual: 0,
    stack: (new Error()).stack,
    name: fn.name || '<anonymous>'
  };

  context[field] = criteria;

  // add the exit listener only once to avoid listener leak warnings
  if (mustCallChecks.length === 0) test.onFinish(function() { runCallChecks(0); });

  mustCallChecks.push(context);

  return function() {
    context.actual++;
    return fn.apply(this, arguments);
  };
}

exports.mustNotCall = function(msg) {
  return function mustNotCall() {
    assert.fail(msg || 'function should not have been called');
  };
};

function filter(arr, fn) {
  if (arr.filter) return arr.filter(fn);
  var filtered = [];
  for (var i = 0; i < arr.length; i++) {
    if (fn(arr[i], i, arr)) filtered.push(arr[i]);
  }
  return filtered
}


code:
'use strict';
var assert = require('assert');
var EventEmitter = require('../');

var EE = new EventEmitter();

assert.throws(function () {
  EE.emit('error', 'Accepts a string');
}, 'Error: Unhandled error. (Accepts a string)');

assert.throws(function () {
  EE.emit('error', { message: 'Error!' });
}, 'Unhandled error. ([object Object])');


code:
'use strict';

var EventEmitter = require('../');
var assert = require('assert');

var EE = new EventEmitter();
var m = function() {};
EE.on('foo', function() {});
assert.equal(1, EE.eventNames().length);
assert.equal('foo', EE.eventNames()[0]);
EE.on('bar', m);
assert.equal(2, EE.eventNames().length);
assert.equal('foo', EE.eventNames()[0]);
assert.equal('bar', EE.eventNames()[1]);
EE.removeListener('bar', m);
assert.equal(1, EE.eventNames().length);
assert.equal('foo', EE.eventNames()[0]);

if (typeof Symbol !== 'undefined') {
  var s = Symbol('s');
  EE.on(s, m);
  assert.equal(2, EE.eventNames().length);
  assert.equal('foo', EE.eventNames()[0]);
  assert.equal(s, EE.eventNames()[1]);
  EE.removeListener(s, m);
  assert.equal(1, EE.eventNames().length);
  assert.equal('foo', EE.eventNames()[0]);
}


code:
'use strict';

var common = require('./common');
var EventEmitter = require('../').EventEmitter;
var once = require('../').once;
var has = require('has');
var assert = require('assert');

function Event(type) {
  this.type = type;
}

function EventTargetMock() {
  this.events = {};

  this.addEventListener = common.mustCall(this.addEventListener);
  this.removeEventListener = common.mustCall(this.removeEventListener);
}

EventTargetMock.prototype.addEventListener = function addEventListener(name, listener, options) {
  if (!(name in this.events)) {
    this.events[name] = { listeners: [], options: options || {} }
  }
  this.events[name].listeners.push(listener);
};

EventTargetMock.prototype.removeEventListener = function removeEventListener(name, callback) {
  if (!(name in this.events)) {
    return;
  }
  var event = this.events[name];
  var stack = event.listeners;

  for (var i = 0, l = stack.length; i < l; i++) {
    if (stack[i] === callback) {
      stack.splice(i, 1);
      if (stack.length === 0) {
        delete this.events[name];
      }
      return;
    }
  }
};

EventTargetMock.prototype.dispatchEvent = function dispatchEvent(arg) {
  if (!(arg.type in this.events)) {
    return true;
  }

  var event = this.events[arg.type];
  var stack = event.listeners.slice();

  for (var i = 0, l = stack.length; i < l; i++) {
    stack[i].call(null, arg);
    if (event.options.once) {
      this.removeEventListener(arg.type, stack[i]);
    }
  }
  return !arg.defaultPrevented;
};

function onceAnEvent() {
  var ee = new EventEmitter();

  process.nextTick(function () {
    ee.emit('myevent', 42);
  });

  return once(ee, 'myevent').then(function (args) {
    var value = args[0]
    assert.strictEqual(value, 42);
    assert.strictEqual(ee.listenerCount('error'), 0);
    assert.strictEqual(ee.listenerCount('myevent'), 0);
  });
}

function onceAnEventWithTwoArgs() {
  var ee = new EventEmitter();

  process.nextTick(function () {
    ee.emit('myevent', 42, 24);
  });

  return once(ee, 'myevent').then(function (value) {
    assert.strictEqual(value.length, 2);
    assert.strictEqual(value[0], 42);
    assert.strictEqual(value[1], 24);
  });
}

function catchesErrors() {
  var ee = new EventEmitter();

  var expected = new Error('kaboom');
  var err;
  process.nextTick(function () {
    ee.emit('error', expected);
  });

  return once(ee, 'myevent').then(function () {
    throw new Error('should reject')
  }, function (err) {
    assert.strictEqual(err, expected);
    assert.strictEqual(ee.listenerCount('error'), 0);
    assert.strictEqual(ee.listenerCount('myevent'), 0);
  });
}

function stopListeningAfterCatchingError() {
  var ee = new EventEmitter();

  var expected = new Error('kaboom');
  var err;
  process.nextTick(function () {
    ee.emit('error', expected);
    ee.emit('myevent', 42, 24);
  });

  // process.on('multipleResolves', common.mustNotCall());

  return once(ee, 'myevent').then(common.mustNotCall, function (err) {
    // process.removeAllListeners('multipleResolves');
    assert.strictEqual(err, expected);
    assert.strictEqual(ee.listenerCount('error'), 0);
    assert.strictEqual(ee.listenerCount('myevent'), 0);
  });
}

function onceError() {
  var ee = new EventEmitter();

  var expected = new Error('kaboom');
  process.nextTick(function () {
    ee.emit('error', expected);
  });

  var promise = once(ee, 'error');
  assert.strictEqual(ee.listenerCount('error'), 1);
  return promise.then(function (args) {
    var err = args[0]
    assert.strictEqual(err, expected);
    assert.strictEqual(ee.listenerCount('error'), 0);
    assert.strictEqual(ee.listenerCount('myevent'), 0);
  });
}

function onceWithEventTarget() {
  var et = new EventTargetMock();
  var event = new Event('myevent');
  process.nextTick(function () {
    et.dispatchEvent(event);
  });
  return once(et, 'myevent').then(function (args) {
    var value = args[0];
    assert.strictEqual(value, event);
    assert.strictEqual(has(et.events, 'myevent'), false);
  });
}

function onceWithEventTargetError() {
  var et = new EventTargetMock();
  var error = new Event('error');
  process.nextTick(function () {
    et.dispatchEvent(error);
  });
  return once(et, 'error').then(function (args) {
    var err = args[0];
    assert.strictEqual(err, error);
    assert.strictEqual(has(et.events, 'error'), false);
  });
}

function prioritizesEventEmitter() {
  var ee = new EventEmitter();
  ee.addEventListener = assert.fail;
  ee.removeAllListeners = assert.fail;
  process.nextTick(function () {
    ee.emit('foo');
  });
  return once(ee, 'foo');
}

var allTests = [
  onceAnEvent(),
  onceAnEventWithTwoArgs(),
  catchesErrors(),
  stopListeningAfterCatchingError(),
  onceError(),
  onceWithEventTarget(),
  onceWithEventTargetError(),
  prioritizesEventEmitter()
];

var hasBrowserEventTarget = false;
try {
  hasBrowserEventTarget = typeof (new window.EventTarget().addEventListener) === 'function' &&
    new window.Event('xyz').type === 'xyz';
} catch (err) {}

if (hasBrowserEventTarget) {
  var onceWithBrowserEventTarget = function onceWithBrowserEventTarget() {
    var et = new window.EventTarget();
    var event = new window.Event('myevent');
    process.nextTick(function () {
      et.dispatchEvent(event);
    });
    return once(et, 'myevent').then(function (args) {
      var value = args[0];
      assert.strictEqual(value, event);
      assert.strictEqual(has(et.events, 'myevent'), false);
    });
  }

  var onceWithBrowserEventTargetError = function onceWithBrowserEventTargetError() {
    var et = new window.EventTarget();
    var error = new window.Event('error');
    process.nextTick(function () {
      et.dispatchEvent(error);
    });
    return once(et, 'error').then(function (args) {
      var err = args[0];
      assert.strictEqual(err, error);
      assert.strictEqual(has(et.events, 'error'), false);
    });
  }

  common.test.comment('Testing with browser built-in EventTarget');
  allTests.push([
    onceWithBrowserEventTarget(),
    onceWithBrowserEventTargetError()
  ]);
}

module.exports = Promise.all(allTests);


code:
var test = require('tape');
var functionsHaveNames = require('functions-have-names');
var hasSymbols = require('has-symbols');

require('./legacy-compat');
var common = require('./common');

// we do this to easily wrap each file in a mocha test
// and also have browserify be able to statically analyze this file
var orig_require = require;
var require = function(file) {
    test(file, function(t) {
        // Store the tape object so tests can access it.
        t.on('end', function () { delete common.test; });
        common.test = t;

        try {
          var exp = orig_require(file);
          if (exp && exp.then) {
            exp.then(function () { t.end(); }, t.fail);
            return;
          }
        } catch (err) {
          t.fail(err);
        }
        t.end();
    });
};

require('./add-listeners.js');
require('./check-listener-leaks.js');
require('./errors.js');
require('./events-list.js');
if (typeof Promise === 'function') {
  require('./events-once.js');
} else {
  // Promise support is not available.
  test('./events-once.js', { skip: true }, function () {});
}
require('./listener-count.js');
require('./listeners-side-effects.js');
require('./listeners.js');
require('./max-listeners.js');
if (functionsHaveNames()) {
  require('./method-names.js');
} else {
  // Function.name is not supported in IE
  test('./method-names.js', { skip: true }, function () {});
}
require('./modify-in-emit.js');
require('./num-args.js');
require('./once.js');
require('./prepend.js');
require('./set-max-listeners-side-effects.js');
require('./special-event-names.js');
require('./subclass.js');
if (hasSymbols()) {
  require('./symbols.js');
} else {
  // Symbol is not available.
  test('./symbols.js', { skip: true }, function () {});
}
require('./remove-all-listeners.js');
require('./remove-listeners.js');


code:
// sigh... life is hard
if (!global.console) {
    console = {}
}

var fns = ['log', 'error', 'trace'];
for (var i=0 ; i<fns.length ; ++i) {
    var fn = fns[i];
    if (!console[fn]) {
        console[fn] = function() {};
    }
}

if (!Array.isArray) {
    Array.isArray = require('isarray');
}


code:
// 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.

require('./common');
var assert = require('assert');
var EventEmitter = require('../');

var emitter = new EventEmitter();
emitter.on('foo', function() {});
emitter.on('foo', function() {});
emitter.on('baz', function() {});
// Allow any type
emitter.on(123, function() {});

assert.strictEqual(EventEmitter.listenerCount(emitter, 'foo'), 2);
assert.strictEqual(emitter.listenerCount('foo'), 2);
assert.strictEqual(emitter.listenerCount('bar'), 0);
assert.strictEqual(emitter.listenerCount('baz'), 1);
assert.strictEqual(emitter.listenerCount(123), 1);


code:
// 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.

require('./common');
var assert = require('assert');

var EventEmitter = require('../').EventEmitter;

var e = new EventEmitter();
var fl;  // foo listeners

fl = e.listeners('foo');
assert.ok(Array.isArray(fl));
assert.strictEqual(fl.length, 0);
if (Object.create) assert.ok(!(e._events instanceof Object));
assert.strictEqual(Object.keys(e._events).length, 0);

e.on('foo', assert.fail);
fl = e.listeners('foo');
assert.strictEqual(e._events.foo, assert.fail);
assert.ok(Array.isArray(fl));
assert.strictEqual(fl.length, 1);
assert.strictEqual(fl[0], assert.fail);

e.listeners('bar');

e.on('foo', assert.ok);
fl = e.listeners('foo');

assert.ok(Array.isArray(e._events.foo));
assert.strictEqual(e._events.foo.length, 2);
assert.strictEqual(e._events.foo[0], assert.fail);
assert.strictEqual(e._events.foo[1], assert.ok);

assert.ok(Array.isArray(fl));
assert.strictEqual(fl.length, 2);
assert.strictEqual(fl[0], assert.fail);
assert.strictEqual(fl[1], assert.ok);


code:
// 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.

require('./common');
var assert = require('assert');
var events = require('../');
var util = require('util');

function listener() {}
function listener2() {}
function listener3() {
  return 0;
}
function listener4() {
  return 1;
}

function TestStream() {}
util.inherits(TestStream, events.EventEmitter);

{
  var ee = new events.EventEmitter();
  ee.on('foo', listener);
  var fooListeners = ee.listeners('foo');

  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], listener);

  ee.removeAllListeners('foo');
  listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);

  assert.ok(Array.isArray(fooListeners));
  assert.strictEqual(fooListeners.length, 1);
  assert.strictEqual(fooListeners[0], listener);
}

{
  var ee = new events.EventEmitter();
  ee.on('foo', listener);

  var eeListenersCopy = ee.listeners('foo');
  assert.ok(Array.isArray(eeListenersCopy));
  assert.strictEqual(eeListenersCopy.length, 1);
  assert.strictEqual(eeListenersCopy[0], listener);

  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], listener);

  eeListenersCopy.push(listener2);
  listeners = ee.listeners('foo');

  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], listener);

  assert.strictEqual(eeListenersCopy.length, 2);
  assert.strictEqual(eeListenersCopy[0], listener);
  assert.strictEqual(eeListenersCopy[1], listener2);
}

{
  var ee = new events.EventEmitter();
  ee.on('foo', listener);
  var eeListenersCopy = ee.listeners('foo');
  ee.on('foo', listener2);

  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 2);
  assert.strictEqual(listeners[0], listener);
  assert.strictEqual(listeners[1], listener2);

  assert.ok(Array.isArray(eeListenersCopy));
  assert.strictEqual(eeListenersCopy.length, 1);
  assert.strictEqual(eeListenersCopy[0], listener);
}

{
  var ee = new events.EventEmitter();
  ee.once('foo', listener);
  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], listener);
}

{
  var ee = new events.EventEmitter();
  ee.on('foo', listener);
  ee.once('foo', listener2);

  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 2);
  assert.strictEqual(listeners[0], listener);
  assert.strictEqual(listeners[1], listener2);
}

{
  var ee = new events.EventEmitter();
  ee._events = undefined;
  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
}

{
  var s = new TestStream();
  var listeners = s.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
}

{
  var ee = new events.EventEmitter();
  ee.on('foo', listener);
  var wrappedListener = ee.rawListeners('foo');
  assert.strictEqual(wrappedListener.length, 1);
  assert.strictEqual(wrappedListener[0], listener);
  assert.notStrictEqual(wrappedListener, ee.rawListeners('foo'));
  ee.once('foo', listener);
  var wrappedListeners = ee.rawListeners('foo');
  assert.strictEqual(wrappedListeners.length, 2);
  assert.strictEqual(wrappedListeners[0], listener);
  assert.notStrictEqual(wrappedListeners[1], listener);
  assert.strictEqual(wrappedListeners[1].listener, listener);
  assert.notStrictEqual(wrappedListeners, ee.rawListeners('foo'));
  ee.emit('foo');
  assert.strictEqual(wrappedListeners.length, 2);
  assert.strictEqual(wrappedListeners[1].listener, listener);
}

{
  var ee = new events.EventEmitter();
  ee.once('foo', listener3);
  ee.on('foo', listener4);
  var rawListeners = ee.rawListeners('foo');
  assert.strictEqual(rawListeners.length, 2);
  assert.strictEqual(rawListeners[0](), 0);
  var rawListener = ee.rawListeners('foo');
  assert.strictEqual(rawListener.length, 1);
  assert.strictEqual(rawListener[0](), 1);
}


code:
// 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.

var common = require('./common');
var assert = require('assert');
var events = require('../');
var e = new events.EventEmitter();

var hasDefineProperty = !!Object.defineProperty;
try { Object.defineProperty({}, 'x', { value: 0 }); } catch (err) { hasDefineProperty = false }

e.on('maxListeners', common.mustCall());

// Should not corrupt the 'maxListeners' queue.
e.setMaxListeners(42);

var throwsObjs = [NaN, -1, 'and even this'];
var maxError = /^RangeError: The value of "n" is out of range\. It must be a non-negative number\./;
var defError = /^RangeError: The value of "defaultMaxListeners" is out of range\. It must be a non-negative number\./;

for (var i = 0; i < throwsObjs.length; i++) {
  var obj = throwsObjs[i];
  assert.throws(function() { e.setMaxListeners(obj); }, maxError);
  if (hasDefineProperty) {
    assert.throws(function() { events.defaultMaxListeners = obj; }, defError);
  }
}

e.emit('maxListeners');


code:
// 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.

'use strict';
require('./common');
var assert = require('assert');
var events = require('../');

var E = events.EventEmitter.prototype;
assert.strictEqual(E.constructor.name, 'EventEmitter');
assert.strictEqual(E.on, E.addListener);  // Same method.
assert.strictEqual(E.off, E.removeListener);  // Same method.
Object.getOwnPropertyNames(E).forEach(function(name) {
  if (name === 'constructor' || name === 'on' || name === 'off') return;
  if (typeof E[name] !== 'function') return;
  assert.strictEqual(E[name].name, name);
});


code:
// 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.

require('./common');
var assert = require('assert');
var events = require('../');

var callbacks_called = [];

var e = new events.EventEmitter();

function callback1() {
  callbacks_called.push('callback1');
  e.on('foo', callback2);
  e.on('foo', callback3);
  e.removeListener('foo', callback1);
}

function callback2() {
  callbacks_called.push('callback2');
  e.removeListener('foo', callback2);
}

function callback3() {
  callbacks_called.push('callback3');
  e.removeListener('foo', callback3);
}

e.on('foo', callback1);
assert.strictEqual(e.listeners('foo').length, 1);

e.emit('foo');
assert.strictEqual(e.listeners('foo').length, 2);
assert.ok(Array.isArray(callbacks_called));
assert.strictEqual(callbacks_called.length, 1);
assert.strictEqual(callbacks_called[0], 'callback1');

e.emit('foo');
assert.strictEqual(e.listeners('foo').length, 0);
assert.ok(Array.isArray(callbacks_called));
assert.strictEqual(callbacks_called.length, 3);
assert.strictEqual(callbacks_called[0], 'callback1');
assert.strictEqual(callbacks_called[1], 'callback2');
assert.strictEqual(callbacks_called[2], 'callback3');

e.emit('foo');
assert.strictEqual(e.listeners('foo').length, 0);
assert.ok(Array.isArray(callbacks_called));
assert.strictEqual(callbacks_called.length, 3);
assert.strictEqual(callbacks_called[0], 'callback1');
assert.strictEqual(callbacks_called[1], 'callback2');
assert.strictEqual(callbacks_called[2], 'callback3');

e.on('foo', callback1);
e.on('foo', callback2);
assert.strictEqual(e.listeners('foo').length, 2);
e.removeAllListeners('foo');
assert.strictEqual(e.listeners('foo').length, 0);

// Verify that removing callbacks while in emit allows emits to propagate to
// all listeners
callbacks_called = [];

e.on('foo', callback2);
e.on('foo', callback3);
assert.strictEqual(2, e.listeners('foo').length);
e.emit('foo');
assert.ok(Array.isArray(callbacks_called));
assert.strictEqual(callbacks_called.length, 2);
assert.strictEqual(callbacks_called[0], 'callback2');
assert.strictEqual(callbacks_called[1], 'callback3');
assert.strictEqual(0, e.listeners('foo').length);


code:
// 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.

require('./common');
var assert = require('assert');
var events = require('../');

var e = new events.EventEmitter();
var num_args_emitted = [];

e.on('numArgs', function() {
  var numArgs = arguments.length;
  num_args_emitted.push(numArgs);
});

e.on('foo', function() {
  num_args_emitted.push(arguments.length);
});

e.on('foo', function() {
  num_args_emitted.push(arguments.length);
});

e.emit('numArgs');
e.emit('numArgs', null);
e.emit('numArgs', null, null);
e.emit('numArgs', null, null, null);
e.emit('numArgs', null, null, null, null);
e.emit('numArgs', null, null, null, null, null);

e.emit('foo', null, null, null, null);

assert.ok(Array.isArray(num_args_emitted));
assert.strictEqual(num_args_emitted.length, 8);
assert.strictEqual(num_args_emitted[0], 0);
assert.strictEqual(num_args_emitted[1], 1);
assert.strictEqual(num_args_emitted[2], 2);
assert.strictEqual(num_args_emitted[3], 3);
assert.strictEqual(num_args_emitted[4], 4);
assert.strictEqual(num_args_emitted[5], 5);
assert.strictEqual(num_args_emitted[6], 4);
assert.strictEqual(num_args_emitted[6], 4);


code:
// 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.

var common = require('./common');
var assert = require('assert');
var EventEmitter = require('../');

var e = new EventEmitter();

e.once('hello', common.mustCall());

e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');

function remove() {
  assert.fail('once->foo should not be emitted');
}

e.once('foo', remove);
e.removeListener('foo', remove);
e.emit('foo');

e.once('e', common.mustCall(function() {
  e.emit('e');
}));

e.once('e', common.mustCall());

e.emit('e');

// Verify that the listener must be a function
assert.throws(function() {
  var ee = new EventEmitter();

  ee.once('foo', null);
}, /^TypeError: The "listener" argument must be of type Function. Received type object$/);

{
  // once() has different code paths based on the number of arguments being
  // emitted. Verify that all of the cases are covered.
  var maxArgs = 4;

  for (var i = 0; i <= maxArgs; ++i) {
    var ee = new EventEmitter();
    var args = ['foo'];

    for (var j = 0; j < i; ++j)
      args.push(j);

    ee.once('foo', common.mustCall(function() {
      var params = Array.prototype.slice.call(arguments);
      var restArgs = args.slice(1);
      assert.ok(Array.isArray(params));
      assert.strictEqual(params.length, restArgs.length);
      for (var index = 0; index < params.length; index++) {
        var param = params[index];
        assert.strictEqual(param, restArgs[index]);
      }
  	}));

    EventEmitter.prototype.emit.apply(ee, args);
  }
}


code:
'use strict';

var common = require('./common');
var EventEmitter = require('../');
var assert = require('assert');

var myEE = new EventEmitter();
var m = 0;
// This one comes last.
myEE.on('foo', common.mustCall(function () {
  assert.strictEqual(m, 2);
}));

// This one comes second.
myEE.prependListener('foo', common.mustCall(function () {
  assert.strictEqual(m++, 1);
}));

// This one comes first.
myEE.prependOnceListener('foo',
                         common.mustCall(function () {
                           assert.strictEqual(m++, 0);
                         }));

myEE.emit('foo');

// Verify that the listener must be a function
assert.throws(function () {
  var ee = new EventEmitter();
  ee.prependOnceListener('foo', null);
}, 'TypeError: The "listener" argument must be of type Function. Received type object');


code:
// 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.

var common = require('./common');
var assert = require('assert');
var events = require('../');
var test = require('tape');

function expect(expected) {
  var actual = [];
  test.onFinish(function() {
    var sortedActual = actual.sort();
    var sortedExpected = expected.sort();
    assert.strictEqual(sortedActual.length, sortedExpected.length);
    for (var index = 0; index < sortedActual.length; index++) {
      var value = sortedActual[index];
      assert.strictEqual(value, sortedExpected[index]);
    }
  });
  function listener(name) {
    actual.push(name);
  }
  return common.mustCall(listener, expected.length);
}

{
  var ee = new events.EventEmitter();
  var noop = common.mustNotCall();
  ee.on('foo', noop);
  ee.on('bar', noop);
  ee.on('baz', noop);
  ee.on('baz', noop);
  var fooListeners = ee.listeners('foo');
  var barListeners = ee.listeners('bar');
  var bazListeners = ee.listeners('baz');
  ee.on('removeListener', expect(['bar', 'baz', 'baz']));
  ee.removeAllListeners('bar');
  ee.removeAllListeners('baz');

  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], noop);

  listeners = ee.listeners('bar');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
  listeners = ee.listeners('baz');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
  // After calling removeAllListeners(),
  // the old listeners array should stay unchanged.
  assert.strictEqual(fooListeners.length, 1);
  assert.strictEqual(fooListeners[0], noop);
  assert.strictEqual(barListeners.length, 1);
  assert.strictEqual(barListeners[0], noop);
  assert.strictEqual(bazListeners.length, 2);
  assert.strictEqual(bazListeners[0], noop);
  assert.strictEqual(bazListeners[1], noop);
  // After calling removeAllListeners(),
  // new listeners arrays is different from the old.
  assert.notStrictEqual(ee.listeners('bar'), barListeners);
  assert.notStrictEqual(ee.listeners('baz'), bazListeners);
}

{
  var ee = new events.EventEmitter();
  ee.on('foo', common.mustNotCall());
  ee.on('bar', common.mustNotCall());
  // Expect LIFO order
  ee.on('removeListener', expect(['foo', 'bar', 'removeListener']));
  ee.on('removeListener', expect(['foo', 'bar']));
  ee.removeAllListeners();

  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
  listeners = ee.listeners('bar');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
}

{
  var ee = new events.EventEmitter();
  ee.on('removeListener', common.mustNotCall());
  // Check for regression where removeAllListeners() throws when
  // there exists a 'removeListener' listener, but there exists
  // no listeners for the provided event type.
  assert.doesNotThrow(function () { ee.removeAllListeners(ee, 'foo') });
}

{
  var ee = new events.EventEmitter();
  var expectLength = 2;
  ee.on('removeListener', function() {
    assert.strictEqual(expectLength--, this.listeners('baz').length);
  });
  ee.on('baz', common.mustNotCall());
  ee.on('baz', common.mustNotCall());
  ee.on('baz', common.mustNotCall());
  assert.strictEqual(ee.listeners('baz').length, expectLength + 1);
  ee.removeAllListeners('baz');
  assert.strictEqual(ee.listeners('baz').length, 0);
}

{
  var ee = new events.EventEmitter();
  assert.strictEqual(ee, ee.removeAllListeners());
}

{
  var ee = new events.EventEmitter();
  ee._events = undefined;
  assert.strictEqual(ee, ee.removeAllListeners());
}


code:
// 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.

var common = require('./common');
var assert = require('assert');
var EventEmitter = require('../');

var listener1 = function listener1() {};
var listener2 = function listener2() {};

{
  var ee = new EventEmitter();
  ee.on('hello', listener1);
  ee.on('removeListener', common.mustCall(function(name, cb) {
    assert.strictEqual(name, 'hello');
    assert.strictEqual(cb, listener1);
  }));
  ee.removeListener('hello', listener1);
  var listeners = ee.listeners('hello');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
}

{
  var ee = new EventEmitter();
  ee.on('hello', listener1);
  ee.on('removeListener', common.mustNotCall());
  ee.removeListener('hello', listener2);

  var listeners = ee.listeners('hello');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], listener1);
}

{
  var ee = new EventEmitter();
  ee.on('hello', listener1);
  ee.on('hello', listener2);

  var listeners;
  ee.once('removeListener', common.mustCall(function(name, cb) {
    assert.strictEqual(name, 'hello');
    assert.strictEqual(cb, listener1);
    listeners = ee.listeners('hello');
    assert.ok(Array.isArray(listeners));
    assert.strictEqual(listeners.length, 1);
    assert.strictEqual(listeners[0], listener2);
  }));
  ee.removeListener('hello', listener1);
  listeners = ee.listeners('hello');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 1);
  assert.strictEqual(listeners[0], listener2);
  ee.once('removeListener', common.mustCall(function(name, cb) {
    assert.strictEqual(name, 'hello');
    assert.strictEqual(cb, listener2);
    listeners = ee.listeners('hello');
    assert.ok(Array.isArray(listeners));
    assert.strictEqual(listeners.length, 0);
  }));
  ee.removeListener('hello', listener2);
  listeners = ee.listeners('hello');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
}

{
  var ee = new EventEmitter();

  function remove1() {
    assert.fail('remove1 should not have been called');
  }

  function remove2() {
    assert.fail('remove2 should not have been called');
  }

  ee.on('removeListener', common.mustCall(function(name, cb) {
    if (cb !== remove1) return;
    this.removeListener('quux', remove2);
    this.emit('quux');
  }, 2));
  ee.on('quux', remove1);
  ee.on('quux', remove2);
  ee.removeListener('quux', remove1);
}

{
  var ee = new EventEmitter();
  ee.on('hello', listener1);
  ee.on('hello', listener2);

  var listeners;
  ee.once('removeListener', common.mustCall(function(name, cb) {
    assert.strictEqual(name, 'hello');
    assert.strictEqual(cb, listener1);
    listeners = ee.listeners('hello');
    assert.ok(Array.isArray(listeners));
    assert.strictEqual(listeners.length, 1);
    assert.strictEqual(listeners[0], listener2);
    ee.once('removeListener', common.mustCall(function(name, cb) {
      assert.strictEqual(name, 'hello');
      assert.strictEqual(cb, listener2);
      listeners = ee.listeners('hello');
      assert.ok(Array.isArray(listeners));
      assert.strictEqual(listeners.length, 0);
    }));
    ee.removeListener('hello', listener2);
    listeners = ee.listeners('hello');
    assert.ok(Array.isArray(listeners));
    assert.strictEqual(listeners.length, 0);
  }));
  ee.removeListener('hello', listener1);
  listeners = ee.listeners('hello');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 0);
}

{
  var ee = new EventEmitter();
  var listener3 = common.mustCall(function() {
    ee.removeListener('hello', listener4);
  }, 2);
  var listener4 = common.mustCall();

  ee.on('hello', listener3);
  ee.on('hello', listener4);

  // listener4 will still be called although it is removed by listener 3.
  ee.emit('hello');
  // This is so because the interal listener array at time of emit
  // was [listener3,listener4]

  // Interal listener array [listener3]
  ee.emit('hello');
}

{
  var ee = new EventEmitter();

  ee.once('hello', listener1);
  ee.on('removeListener', common.mustCall(function(eventName, listener) {
    assert.strictEqual(eventName, 'hello');
    assert.strictEqual(listener, listener1);
  }));
  ee.emit('hello');
}

{
  var ee = new EventEmitter();

  assert.strictEqual(ee, ee.removeListener('foo', function() {}));
}

// Verify that the removed listener must be a function
assert.throws(function() {
  var ee = new EventEmitter();

  ee.removeListener('foo', null);
}, /^TypeError: The "listener" argument must be of type Function\. Received type object$/);

{
  var ee = new EventEmitter();
  var listener = function() {};
  ee._events = undefined;
  var e = ee.removeListener('foo', listener);
  assert.strictEqual(e, ee);
}

{
  var ee = new EventEmitter();

  ee.on('foo', listener1);
  ee.on('foo', listener2);
  var listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 2);
  assert.strictEqual(listeners[0], listener1);
  assert.strictEqual(listeners[1], listener2);

  ee.removeListener('foo', listener1);
  assert.strictEqual(ee._events.foo, listener2);

  ee.on('foo', listener1);
  listeners = ee.listeners('foo');
  assert.ok(Array.isArray(listeners));
  assert.strictEqual(listeners.length, 2);
  assert.strictEqual(listeners[0], listener2);
  assert.strictEqual(listeners[1], listener1);

  ee.removeListener('foo', listener1);
  assert.strictEqual(ee._events.foo, listener2);
}


code:
// 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.

require('./common');
var assert = require('assert');
var events = require('../');

var e = new events.EventEmitter();

if (Object.create) assert.ok(!(e._events instanceof Object));
assert.strictEqual(Object.keys(e._events).length, 0);
e.setMaxListeners(5);
assert.strictEqual(Object.keys(e._events).length, 0);


code:
'use strict';

var common = require('./common');
var EventEmitter = require('../');
var assert = require('assert');

var ee = new EventEmitter();
var handler = function() {};

assert.strictEqual(ee.eventNames().length, 0);

assert.strictEqual(ee._events.hasOwnProperty, undefined);
assert.strictEqual(ee._events.toString, undefined);

ee.on('__defineGetter__', handler);
ee.on('toString', handler);
ee.on('__proto__', handler);

assert.strictEqual(ee.eventNames()[0], '__defineGetter__');
assert.strictEqual(ee.eventNames()[1], 'toString');

assert.strictEqual(ee.listeners('__defineGetter__').length, 1);
assert.strictEqual(ee.listeners('__defineGetter__')[0], handler);
assert.strictEqual(ee.listeners('toString').length, 1);
assert.strictEqual(ee.listeners('toString')[0], handler);

// Only run __proto__ tests if that property can actually be set
if ({ __proto__: 'ok' }.__proto__ === 'ok') {
  assert.strictEqual(ee.eventNames().length, 3);
  assert.strictEqual(ee.eventNames()[2], '__proto__');
  assert.strictEqual(ee.listeners('__proto__').length, 1);
  assert.strictEqual(ee.listeners('__proto__')[0], handler);

  ee.on('__proto__', common.mustCall(function(val) {
    assert.strictEqual(val, 1);
  }));
  ee.emit('__proto__', 1);

  process.on('__proto__', common.mustCall(function(val) {
    assert.strictEqual(val, 1);
  }));
  process.emit('__proto__', 1);
} else {
  console.log('# skipped __proto__')
}


code:
// 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.

var common = require('./common');
var test = require('tape');
var assert = require('assert');
var EventEmitter = require('../').EventEmitter;
var util = require('util');

util.inherits(MyEE, EventEmitter);

function MyEE(cb) {
  this.once(1, cb);
  this.emit(1);
  this.removeAllListeners();
  EventEmitter.call(this);
}

var myee = new MyEE(common.mustCall());

util.inherits(ErrorEE, EventEmitter);
function ErrorEE() {
  this.emit('error', new Error('blerg'));
}

assert.throws(function() {
  new ErrorEE();
}, /blerg/);

test.onFinish(function() {
  assert.ok(!(myee._events instanceof Object));
  assert.strictEqual(Object.keys(myee._events).length, 0);
});

function MyEE2() {
  EventEmitter.call(this);
}

MyEE2.prototype = new EventEmitter();

var ee1 = new MyEE2();
var ee2 = new MyEE2();

ee1.on('x', function() {});

assert.strictEqual(ee2.listenerCount('x'), 0);


code:
'use strict';

var common = require('./common');
var EventEmitter = require('../');
var assert = require('assert');

var ee = new EventEmitter();
var foo = Symbol('foo');
var listener = common.mustCall();

ee.on(foo, listener);
assert.strictEqual(ee.listeners(foo).length, 1);
assert.strictEqual(ee.listeners(foo)[0], listener);

ee.emit(foo);

ee.removeAllListeners();
assert.strictEqual(ee.listeners(foo).length, 0);

ee.on(foo, listener);
assert.strictEqual(ee.listeners(foo).length, 1);
assert.strictEqual(ee.listeners(foo)[0], listener);

ee.removeListener(foo, listener);
assert.strictEqual(ee.listeners(foo).length, 0);


code:
'use strict';
const path = require('path');
const childProcess = require('child_process');
const crossSpawn = require('cross-spawn');
const stripEof = require('strip-eof');
const npmRunPath = require('npm-run-path');
const isStream = require('is-stream');
const _getStream = require('get-stream');
const pFinally = require('p-finally');
const onExit = require('signal-exit');
const errname = require('./lib/errname');
const stdio = require('./lib/stdio');

const TEN_MEGABYTES = 1000 * 1000 * 10;

function handleArgs(cmd, args, opts) {
	let parsed;

	opts = Object.assign({
		extendEnv: true,
		env: {}
	}, opts);

	if (opts.extendEnv) {
		opts.env = Object.assign({}, process.env, opts.env);
	}

	if (opts.__winShell === true) {
		delete opts.__winShell;
		parsed = {
			command: cmd,
			args,
			options: opts,
			file: cmd,
			original: {
				cmd,
				args
			}
		};
	} else {
		parsed = crossSpawn._parse(cmd, args, opts);
	}

	opts = Object.assign({
		maxBuffer: TEN_MEGABYTES,
		buffer: true,
		stripEof: true,
		preferLocal: true,
		localDir: parsed.options.cwd || process.cwd(),
		encoding: 'utf8',
		reject: true,
		cleanup: true
	}, parsed.options);

	opts.stdio = stdio(opts);

	if (opts.preferLocal) {
		opts.env = npmRunPath.env(Object.assign({}, opts, {cwd: opts.localDir}));
	}

	if (opts.detached) {
		// #115
		opts.cleanup = false;
	}

	if (process.platform === 'win32' && path.basename(parsed.command) === 'cmd.exe') {
		// #116
		parsed.args.unshift('/q');
	}

	return {
		cmd: parsed.command,
		args: parsed.args,
		opts,
		parsed
	};
}

function handleInput(spawned, input) {
	if (input === null || input === undefined) {
		return;
	}

	if (isStream(input)) {
		input.pipe(spawned.stdin);
	} else {
		spawned.stdin.end(input);
	}
}

function handleOutput(opts, val) {
	if (val && opts.stripEof) {
		val = stripEof(val);
	}

	return val;
}

function handleShell(fn, cmd, opts) {
	let file = '/bin/sh';
	let args = ['-c', cmd];

	opts = Object.assign({}, opts);

	if (process.platform === 'win32') {
		opts.__winShell = true;
		file = process.env.comspec || 'cmd.exe';
		args = ['/s', '/c', `"${cmd}"`];
		opts.windowsVerbatimArguments = true;
	}

	if (opts.shell) {
		file = opts.shell;
		delete opts.shell;
	}

	return fn(file, args, opts);
}

function getStream(process, stream, {encoding, buffer, maxBuffer}) {
	if (!process[stream]) {
		return null;
	}

	let ret;

	if (!buffer) {
		// TODO: Use `ret = util.promisify(stream.finished)(process[stream]);` when targeting Node.js 10
		ret = new Promise((resolve, reject) => {
			process[stream]
				.once('end', resolve)
				.once('error', reject);
		});
	} else if (encoding) {
		ret = _getStream(process[stream], {
			encoding,
			maxBuffer
		});
	} else {
		ret = _getStream.buffer(process[stream], {maxBuffer});
	}

	return ret.catch(err => {
		err.stream = stream;
		err.message = `${stream} ${err.message}`;
		throw err;
	});
}

function makeError(result, options) {
	const {stdout, stderr} = result;

	let err = result.error;
	const {code, signal} = result;

	const {parsed, joinedCmd} = options;
	const timedOut = options.timedOut || false;

	if (!err) {
		let output = '';

		if (Array.isArray(parsed.opts.stdio)) {
			if (parsed.opts.stdio[2] !== 'inherit') {
				output += output.length > 0 ? stderr : `\n${stderr}`;
			}

			if (parsed.opts.stdio[1] !== 'inherit') {
				output += `\n${stdout}`;
			}
		} else if (parsed.opts.stdio !== 'inherit') {
			output = `\n${stderr}${stdout}`;
		}

		err = new Error(`Command failed: ${joinedCmd}${output}`);
		err.code = code < 0 ? errname(code) : code;
	}

	err.stdout = stdout;
	err.stderr = stderr;
	err.failed = true;
	err.signal = signal || null;
	err.cmd = joinedCmd;
	err.timedOut = timedOut;

	return err;
}

function joinCmd(cmd, args) {
	let joinedCmd = cmd;

	if (Array.isArray(args) && args.length > 0) {
		joinedCmd += ' ' + args.join(' ');
	}

	return joinedCmd;
}

module.exports = (cmd, args, opts) => {
	const parsed = handleArgs(cmd, args, opts);
	const {encoding, buffer, maxBuffer} = parsed.opts;
	const joinedCmd = joinCmd(cmd, args);

	let spawned;
	try {
		spawned = childProcess.spawn(parsed.cmd, parsed.args, parsed.opts);
	} catch (err) {
		return Promise.reject(err);
	}

	let removeExitHandler;
	if (parsed.opts.cleanup) {
		removeExitHandler = onExit(() => {
			spawned.kill();
		});
	}

	let timeoutId = null;
	let timedOut = false;

	const cleanup = () => {
		if (timeoutId) {
			clearTimeout(timeoutId);
			timeoutId = null;
		}

		if (removeExitHandler) {
			removeExitHandler();
		}
	};

	if (parsed.opts.timeout > 0) {
		timeoutId = setTimeout(() => {
			timeoutId = null;
			timedOut = true;
			spawned.kill(parsed.opts.killSignal);
		}, parsed.opts.timeout);
	}

	const processDone = new Promise(resolve => {
		spawned.on('exit', (code, signal) => {
			cleanup();
			resolve({code, signal});
		});

		spawned.on('error', err => {
			cleanup();
			resolve({error: err});
		});

		if (spawned.stdin) {
			spawned.stdin.on('error', err => {
				cleanup();
				resolve({error: err});
			});
		}
	});

	function destroy() {
		if (spawned.stdout) {
			spawned.stdout.destroy();
		}

		if (spawned.stderr) {
			spawned.stderr.destroy();
		}
	}

	const handlePromise = () => pFinally(Promise.all([
		processDone,
		getStream(spawned, 'stdout', {encoding, buffer, maxBuffer}),
		getStream(spawned, 'stderr', {encoding, buffer, maxBuffer})
	]).then(arr => {
		const result = arr[0];
		result.stdout = arr[1];
		result.stderr = arr[2];

		if (result.error || result.code !== 0 || result.signal !== null) {
			const err = makeError(result, {
				joinedCmd,
				parsed,
				timedOut
			});

			// TODO: missing some timeout logic for killed
			// https://github.com/nodejs/node/blob/master/lib/child_process.js#L203
			// err.killed = spawned.killed || killed;
			err.killed = err.killed || spawned.killed;

			if (!parsed.opts.reject) {
				return err;
			}

			throw err;
		}

		return {
			stdout: handleOutput(parsed.opts, result.stdout),
			stderr: handleOutput(parsed.opts, result.stderr),
			code: 0,
			failed: false,
			killed: false,
			signal: null,
			cmd: joinedCmd,
			timedOut: false
		};
	}), destroy);

	crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed);

	handleInput(spawned, parsed.opts.input);

	spawned.then = (onfulfilled, onrejected) => handlePromise().then(onfulfilled, onrejected);
	spawned.catch = onrejected => handlePromise().catch(onrejected);

	return spawned;
};

// TODO: set `stderr: 'ignore'` when that option is implemented
module.exports.stdout = (...args) => module.exports(...args).then(x => x.stdout);

// TODO: set `stdout: 'ignore'` when that option is implemented
module.exports.stderr = (...args) => module.exports(...args).then(x => x.stderr);

module.exports.shell = (cmd, opts) => handleShell(module.exports, cmd, opts);

module.exports.sync = (cmd, args, opts) => {
	const parsed = handleArgs(cmd, args, opts);
	const joinedCmd = joinCmd(cmd, args);

	if (isStream(parsed.opts.input)) {
		throw new TypeError('The `input` option cannot be a stream in sync mode');
	}

	const result = childProcess.spawnSync(parsed.cmd, parsed.args, parsed.opts);
	result.code = result.status;

	if (result.error || result.status !== 0 || result.signal !== null) {
		const err = makeError(result, {
			joinedCmd,
			parsed
		});

		if (!parsed.opts.reject) {
			return err;
		}

		throw err;
	}

	return {
		stdout: handleOutput(parsed.opts, result.stdout),
		stderr: handleOutput(parsed.opts, result.stderr),
		code: 0,
		failed: false,
		signal: null,
		cmd: joinedCmd,
		timedOut: false
	};
};

module.exports.shellSync = (cmd, opts) => handleShell(module.exports.sync, cmd, opts);


code:
'use strict';
// Older verions of Node.js might not have `util.getSystemErrorName()`.
// In that case, fall back to a deprecated internal.
const util = require('util');

let uv;

if (typeof util.getSystemErrorName === 'function') {
	module.exports = util.getSystemErrorName;
} else {
	try {
		uv = process.binding('uv');

		if (typeof uv.errname !== 'function') {
			throw new TypeError('uv.errname is not a function');
		}
	} catch (err) {
		console.error('execa/lib/errname: unable to establish process.binding(\'uv\')', err);
		uv = null;
	}

	module.exports = code => errname(uv, code);
}

// Used for testing the fallback behavior
module.exports.__test__ = errname;

function errname(uv, code) {
	if (uv) {
		return uv.errname(code);
	}

	if (!(code < 0)) {
		throw new Error('err >= 0');
	}

	return `Unknown system error ${code}`;
}


code:
'use strict';
const alias = ['stdin', 'stdout', 'stderr'];

const hasAlias = opts => alias.some(x => Boolean(opts[x]));

module.exports = opts => {
	if (!opts) {
		return null;
	}

	if (opts.stdio && hasAlias(opts)) {
		throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${alias.map(x => `\`${x}\``).join(', ')}`);
	}

	if (typeof opts.stdio === 'string') {
		return opts.stdio;
	}

	const stdio = opts.stdio || [];

	if (!Array.isArray(stdio)) {
		throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``);
	}

	const result = [];
	const len = Math.max(stdio.length, alias.length);

	for (let i = 0; i < len; i++) {
		let value = null;

		if (stdio[i] !== undefined) {
			value = stdio[i];
		} else if (opts[alias[i]] !== undefined) {
			value = opts[alias[i]];
		}

		result[i] = value;
	}

	return result;
};


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../semver/bin/semver" "$@"
else
  exec node  "$basedir/../semver/bin/semver" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\semver\bin\semver" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../semver/bin/semver" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../semver/bin/semver" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../semver/bin/semver" $args
  } else {
    & "node$exe"  "$basedir/../semver/bin/semver" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  exec "$basedir/node"  "$basedir/../which/bin/which" "$@"
else
  exec node  "$basedir/../which/bin/which" "$@"
fi


code:
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\which\bin\which" %*


code:
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
  # Fix case when both the Windows and Linux builds of Node
  # are installed in the same directory
  $exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "$basedir/node$exe"  "$basedir/../which/bin/which" $args
  } else {
    & "$basedir/node$exe"  "$basedir/../which/bin/which" $args
  }
  $ret=$LASTEXITCODE
} else {
  # Support pipeline input
  if ($MyInvocation.ExpectingInput) {
    $input | & "node$exe"  "$basedir/../which/bin/which" $args
  } else {
    & "node$exe"  "$basedir/../which/bin/which" $args
  }
  $ret=$LASTEXITCODE
}
exit $ret


code:
# Change Log

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [6.0.5](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.4...v6.0.5) (2018-03-02)

### Bug Fixes

* avoid using deprecated Buffer constructor ([#94](https://github.com/moxystudio/node-cross-spawn/issues/94)) ([d5770df](https://github.com/moxystudio/node-cross-spawn/commit/d5770df)), closes [/nodejs.org/api/deprecations.html#deprecations_dep0005](https://github.com//nodejs.org/api/deprecations.html/issues/deprecations_dep0005)

## [6.0.4](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.3...v6.0.4) (2018-01-31)

### Bug Fixes

* fix paths being incorrectly normalized on unix ([06ee3c6](https://github.com/moxystudio/node-cross-spawn/commit/06ee3c6)), closes [#90](https://github.com/moxystudio/node-cross-spawn/issues/90)

## [6.0.3](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.2...v6.0.3) (2018-01-23)

## [6.0.2](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.1...v6.0.2) (2018-01-23)

## [6.0.1](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.0...v6.0.1) (2018-01-23)

# [6.0.0](https://github.com/moxystudio/node-cross-spawn/compare/5.1.0...6.0.0) (2018-01-23)

### Bug Fixes

* fix certain arguments not being correctly escaped or causing batch syntax error ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10)), closes [#82](https://github.com/moxystudio/node-cross-spawn/issues/82) [#51](https://github.com/moxystudio/node-cross-spawn/issues/51)
* fix commands as posix relatixe paths not working correctly, e.g.: `./my-command` ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
* fix `options` argument being mutated ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
* fix commands resolution when PATH was actually Path ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))

### Features

* improve compliance with node's ENOENT errors ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
* improve detection of node's shell option support ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))

### Chores

* upgrade tooling
* upgrate project to es6 (node v4)

### BREAKING CHANGES

* remove support for older nodejs versions, only `node >= 4` is supported

## [5.1.0](https://github.com/moxystudio/node-cross-spawn/compare/5.0.1...5.1.0) (2017-02-26)

### Bug Fixes

* fix `options.shell` support for NodeJS [v4.8](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V4.md#4.8.0)

## [5.0.1](https://github.com/moxystudio/node-cross-spawn/compare/5.0.0...5.0.1) (2016-11-04)

### Bug Fixes

* fix `options.shell` support for NodeJS v7

# [5.0.0](https://github.com/moxystudio/node-cross-spawn/compare/4.0.2...5.0.0) (2016-10-30)

## Features

* add support for `options.shell`
* improve parsing of shebangs by using [`shebang-command`](https://github.com/kevva/shebang-command) module

## Chores

* refactor some code to make it more clear
* update README caveats


code:
# cross-spawn

[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build status][appveyor-image]][appveyor-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] [![Greenkeeper badge][greenkeeper-image]][greenkeeper-url]

[npm-url]:https://npmjs.org/package/cross-spawn
[downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg
[npm-image]:http://img.shields.io/npm/v/cross-spawn.svg
[travis-url]:https://travis-ci.org/moxystudio/node-cross-spawn
[travis-image]:http://img.shields.io/travis/moxystudio/node-cross-spawn/master.svg
[appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn
[appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg
[codecov-url]:https://codecov.io/gh/moxystudio/node-cross-spawn
[codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/node-cross-spawn/master.svg
[david-dm-url]:https://david-dm.org/moxystudio/node-cross-spawn
[david-dm-image]:https://img.shields.io/david/moxystudio/node-cross-spawn.svg
[david-dm-dev-url]:https://david-dm.org/moxystudio/node-cross-spawn?type=dev
[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/node-cross-spawn.svg
[greenkeeper-image]:https://badges.greenkeeper.io/moxystudio/node-cross-spawn.svg
[greenkeeper-url]:https://greenkeeper.io/

A cross platform solution to node's spawn and spawnSync.

## Installation

`$ npm install cross-spawn`

## Why

Node has issues when using spawn on Windows:

- It ignores [PATHEXT](https://github.com/joyent/node/issues/2318)
- It does not support [shebangs](https://en.wikipedia.org/wiki/Shebang_(Unix))
- Has problems running commands with [spaces](https://github.com/nodejs/node/issues/7367)
- Has problems running commands with posix relative paths (e.g.: `./my-folder/my-executable`)
- Has an [issue](https://github.com/moxystudio/node-cross-spawn/issues/82) with command shims (files in `node_modules/.bin/`), where arguments with quotes and parenthesis would result in [invalid syntax error](https://github.com/moxystudio/node-cross-spawn/blob/e77b8f22a416db46b6196767bcd35601d7e11d54/test/index.test.js#L149)
- No `options.shell` support on node `<v4.8`

All these issues are handled correctly by `cross-spawn`.
There are some known modules, such as [win-spawn](https://github.com/ForbesLindesay/win-spawn), that try to solve this but they are either broken or provide faulty escaping of shell arguments.

## Usage

Exactly the same way as node's [`spawn`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) or [`spawnSync`](https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options), so it's a drop in replacement.

```js
const spawn = require('cross-spawn');

// Spawn NPM asynchronously
const child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });

// Spawn NPM synchronously
const result = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
```

## Caveats

### Using `options.shell` as an alternative to `cross-spawn`

Starting from node `v4.8`, `spawn` has a `shell` option that allows you run commands from within a shell. This new option solves
the [PATHEXT](https://github.com/joyent/node/issues/2318) issue but:

- It's not supported in node `<v4.8`
- You must manually escape the command and arguments which is very error prone, specially when passing user input
- There are a lot of other unresolved issues from the [Why](#why) section that you must take into account

If you are using the `shell` option to spawn a command in a cross platform way, consider using `cross-spawn` instead. You have been warned.

### `options.shell` support

While `cross-spawn` adds support for `options.shell` in node `<v4.8`, all of its enhancements are disabled.

This mimics the Node.js behavior. More specifically, the command and its arguments will not be automatically escaped nor shebang support will be offered. This is by design because if you are using `options.shell` you are probably targeting a specific platform anyway and you don't want things to get into your way.

### Shebangs support

While `cross-spawn` handles shebangs on Windows, its support is limited. More specifically, it just supports `#!/usr/bin/env <program>` where `<program>` must not contain any arguments.
If you would like to have the shebang support improved, feel free to contribute via a pull-request.

Remember to always test your code on Windows!

## Tests

`$ npm test`
`$ npm test -- --watch` during development

## License

Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).


code:
'use strict';

const cp = require('child_process');
const parse = require('./lib/parse');
const enoent = require('./lib/enoent');

function spawn(command, args, options) {
    // Parse the arguments
    const parsed = parse(command, args, options);

    // Spawn the child process
    const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);

    // Hook into child process "exit" event to emit an error if the command
    // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
    enoent.hookChildProcess(spawned, parsed);

    return spawned;
}

function spawnSync(command, args, options) {
    // Parse the arguments
    const parsed = parse(command, args, options);

    // Spawn the child process
    const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);

    // Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
    result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);

    return result;
}

module.exports = spawn;
module.exports.spawn = spawn;
module.exports.sync = spawnSync;

module.exports._parse = parse;
module.exports._enoent = enoent;


code:
'use strict';

const isWin = process.platform === 'win32';

function notFoundError(original, syscall) {
    return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
        code: 'ENOENT',
        errno: 'ENOENT',
        syscall: `${syscall} ${original.command}`,
        path: original.command,
        spawnargs: original.args,
    });
}

function hookChildProcess(cp, parsed) {
    if (!isWin) {
        return;
    }

    const originalEmit = cp.emit;

    cp.emit = function (name, arg1) {
        // If emitting "exit" event and exit code is 1, we need to check if
        // the command exists and emit an "error" instead
        // See https://github.com/IndigoUnited/node-cross-spawn/issues/16
        if (name === 'exit') {
            const err = verifyENOENT(arg1, parsed, 'spawn');

            if (err) {
                return originalEmit.call(cp, 'error', err);
            }
        }

        return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params
    };
}

function verifyENOENT(status, parsed) {
    if (isWin && status === 1 && !parsed.file) {
        return notFoundError(parsed.original, 'spawn');
    }

    return null;
}

function verifyENOENTSync(status, parsed) {
    if (isWin && status === 1 && !parsed.file) {
        return notFoundError(parsed.original, 'spawnSync');
    }

    return null;
}

module.exports = {
    hookChildProcess,
    verifyENOENT,
    verifyENOENTSync,
    notFoundError,
};