pax_global_header00006660000000000000000000000064134072140420014507gustar00rootroot0000000000000052 comment=4fecc207c07ea73008ed35b7487162ee970202c3 capture-exit-2.0.0/000077500000000000000000000000001340721404200141205ustar00rootroot00000000000000capture-exit-2.0.0/.gitignore000066400000000000000000000000151340721404200161040ustar00rootroot00000000000000node_modules capture-exit-2.0.0/.travis.yml000066400000000000000000000023751340721404200162400ustar00rootroot00000000000000sudo: false language: node_js node_js: - "6" - "8" - "10" - "11" cache: directories: $HOME/.npm before_install: # prevent the npm loading indicator - npm config --global set spin false # if npm version is less 3.0.0 (lets say 1.x or 2.x) we should attempt to upgrade to 3 - if [[ $(npm -v | cut -d '.' -f 1) -lt 3 ]]; then npm i -g npm@^3; fi install: - travis_retry npm install --no-optional script: - npm run test deploy: provider: npm email: stefan.penner+ember-cli@gmail.com api_key: secure: XelwAGywZjqBYEeeiTeQ4mJwm5kT/jSnu8SM8rtq8lYRKDqFkfH1V6SP6UDTxmpErh4EIyMRIrLRCJaokG3nsue7hSjrJq3ycfcqw6WbwWJ1dcvmRTrsF7RTNPIi7T03ui1zTppmL2LL2OyCEyfd3f2aLwxf6fh/Lhy1Y69G672bdT7QzcPwPrydQHW6n7O8ri6LpRlkGua2QjsU6g10nHfgDm78O+oj3z13Ur7YBwfi/GfUHdwawZ9mIdYzOvm6uonfIeGlX0UivM3k+7p1VBeVOI3asd8P8xapziQ8/6rUypfaMQ2L87FzVfL+tD34O3TJEYjDsb5pnkEtj46Od98Mv8boVzcoIqGeSA9L3nLkLDSI46ghLIoZTNCctLN/LLmXihUGlNiErVX6AUaH3VcHh0OvSteixKkvKWuIbetwzHwqbrriabUYZXVUhIp+Z6vyLOMnbdXz4Okd7jut+4HCGRPRVZqzBDIY2nvkRbL3FBqEvaN51jga0E5jsfH5vkPHHfnw3jraYCgmhXCyiyGanSylX3rzOyxOyVDyl8/MwZ1bNBxGfFOpyCrxdWkB7rGOLexogTUAnoBK8t/Udqf/P9rpxZ0lfGs8I7A7tuzLy0vs+fwptJXfHLMHjrNCF7E/D2HsvDZiho+Vz+k8ol+4ajmLynDw8Wn4JcB6kzY= on: tags: true repo: ember-cli/capture-exit capture-exit-2.0.0/README.md000066400000000000000000000023321340721404200153770ustar00rootroot00000000000000# capture-exit [![Build status](https://ci.appveyor.com/api/projects/status/8044m918rwic8b9n/branch/master?svg=true)](https://ci.appveyor.com/project/embercli/capture-exit/branch/master) [![Build Status](https://travis-ci.org/ember-cli/capture-exit.svg?branch=master)](https://travis-ci.org/ember-cli/capture-exit) Allow cooprative async exit handlers, we unfortunately must hijack process.exit. It allows a handler to ensure exit, without that exit handler impeding other similar handlers for example, see: [sindresorhus/ora#27](https://github.com/sindresorhus/ora/issues/27) Differences between `process.on('exit')` and `captureExit.onExit(...)` => https://github.com/ember-cli/capture-exit/issues/12 ### Installation ```sh yarn add capture-exit // or npm install --save capture-exit ``` ### Usage ```js // as early in startup as possible require('capture-exit').captureExit(); // when you want to schedule some work on exit: function onExit() { return something.processWillExit(); // you can return promises, which will pause exit until fulfilled } require('capture-exit').onExit(onExit); // add an exit handler require('capture-exit').offExit(onExit); // allows one to remove an exit handle if it is not longer required ``` capture-exit-2.0.0/appveyor.yml000066400000000000000000000014541340721404200165140ustar00rootroot00000000000000# https://www.appveyor.com/docs/appveyor-yml/ # Test against these versions of Node.js. environment: MOCHA_REPORTER: "mocha-appveyor-reporter" matrix: - nodejs_version: "0.12" - nodejs_version: "4" - nodejs_version: "6" # Install scripts. (runs after repo cloning) install: - git rev-parse HEAD # Get the latest stable version of Node 0.STABLE.latest - ps: Install-Product node $env:nodejs_version # Install PhantomJS - npm install mocha-appveyor-reporter # Typical npm stuff. - npm version - npm install cache: - '%APPDATA%\npm-cache' # Post-install test scripts. test_script: # Output useful info for debugging. - npm version - cmd: npm run test # Don't actually build. build: off # Set build version format here instead of in the admin panel. version: "{build}" capture-exit-2.0.0/example.js000066400000000000000000000007551340721404200161200ustar00rootroot00000000000000// remove the next line, and "cleanup" prints. // var exit = require('./'); exit.captureExit(); require('ora')('Loading unicorns').start().stop(); exit.onExit(function() { console.log('wat'); return new Promise(function(resolve) { console.log('waiting'); setTimeout(function() { console.log('complete!'); }, 1000); }) }); process.on('SIGINT', function() { // never called if ora was ever enabled console.log('cleanup'); }); process.kill(process.pid, 'SIGINT'); capture-exit-2.0.0/index.js000066400000000000000000000101151340721404200155630ustar00rootroot00000000000000var RSVP = require('rsvp'); var exit; var handlers = []; var lastTime; var isExiting = false; process.on('beforeExit', function (code) { if (handlers.length === 0) { return; } var own = lastTime = module.exports._flush(lastTime, code) .finally(function () { // if an onExit handler has called process.exit, do not disturb // `lastTime`. // // Otherwise, clear `lastTime` so that we know to synchronously call the // real `process.exit` with the given exit code, when our captured // `process.exit` is called during a `process.on('exit')` handler // // This is impossible to reason about, don't feel bad. Just look at // test-natural-exit-subprocess-error.js if (own === lastTime) { lastTime = undefined; } }); }); // This exists only for testing module.exports._reset = function () { module.exports.releaseExit(); handlers = []; lastTime = undefined; isExiting = false; firstExitCode = undefined; } /* * To allow cooperative async exit handlers, we unfortunately must hijack * process.exit. * * It allows a handler to ensure exit, without that exit handler impeding other * similar handlers * * for example, see: https://github.com/sindresorhus/ora/issues/27 * */ module.exports.releaseExit = function() { if (exit) { process.exit = exit; exit = null; } }; var firstExitCode; module.exports.captureExit = function() { if (exit) { // already captured, no need to do more work return; } exit = process.exit; process.exit = function(code) { if (handlers.length === 0 && lastTime === undefined) { // synchronously exit. // // We do this brecause either // // 1. The process exited due to a call to `process.exit` but we have no // async work to do because no handlers had been attached. It // doesn't really matter whether we take this branch or not in this // case. // // 2. The process exited naturally. We did our async work during // `beforeExit` and are in this function because someone else has // called `process.exit` during an `on('exit')` hook. The only way // for us to preserve the exit code in this case is to exit // synchronously. // return exit.call(process, code); } if (firstExitCode === undefined) { firstExitCode = code; } var own = lastTime = module.exports._flush(lastTime, firstExitCode) .then(function() { // if another chain has started, let it exit if (own !== lastTime) { return; } exit.call(process, firstExitCode); }) .catch(function(error) { // if another chain has started, let it exit if (own !== lastTime) { throw error; } console.error(error); exit.call(process, 1); }); }; }; module.exports._handlers = handlers; module.exports._flush = function(lastTime, code) { isExiting = true; var work = handlers.splice(0, handlers.length); return RSVP.Promise.resolve(lastTime). then(function() { var firstRejected; return RSVP.allSettled(work.map(function(handler) { return RSVP.resolve(handler.call(null, code)).catch(function(e) { if (!firstRejected) { firstRejected = e; } throw e; }); })).then(function(results) { if (firstRejected) { throw firstRejected; } }); }); }; module.exports.onExit = function(cb) { if (!exit) { throw new Error('Cannot install handler when exit is not captured. Call `captureExit()` first'); } if (isExiting) { throw new Error('Cannot install handler while `onExit` handlers are running.'); } var index = handlers.indexOf(cb); if (index > -1) { return; } handlers.push(cb); }; module.exports.offExit = function(cb) { var index = handlers.indexOf(cb); if (index < 0) { return; } handlers.splice(index, 1); }; module.exports.exit = function() { exit.apply(process, arguments); }; module.exports.listenerCount = function() { return handlers.length; }; capture-exit-2.0.0/package.json000066400000000000000000000014321340721404200164060ustar00rootroot00000000000000{ "name": "capture-exit", "version": "2.0.0", "description": "safely cleanup in signal handlers", "main": "index.js", "scripts": { "test": "mocha test", "test:debug": "mocha debug test" }, "files": [ "index.js" ], "repository": { "type": "git", "url": "git+https://github.com/stefanpenner/capture-exit.git" }, "author": "Stefan Penner ", "license": "ISC", "bugs": { "url": "https://github.com/stefanpenner/capture-exit/issues" }, "homepage": "https://github.com/stefanpenner/capture-exit#readme", "devDependencies": { "chai": "^4.2.0", "execa": "1.0.0", "mocha": "^5.2.0", "ora": "^3.0.0" }, "dependencies": { "rsvp": "^4.8.4" }, "engines": { "node": "6.* || 8.* || >= 10.*" } } capture-exit-2.0.0/process-exit-during-final-exit-subprocess.js000066400000000000000000000002561340721404200246200ustar00rootroot00000000000000'use strict'; // capture-exit-onExit.js var captureExit = require('.'); captureExit.captureExit(); process.on('exit', function() { process.exit(1); }); process.exit(0); capture-exit-2.0.0/test-natural-exit-subprocess-error-exit-from-captures-on-exit.js000066400000000000000000000004361340721404200305050ustar00rootroot00000000000000'use strict'; var capture = require('./'); capture.captureExit(); capture.onExit(function () { console.log('onExit'); process.exit(1); }); capture.onExit(function () { console.log('onExit2'); process.exit(2); }); process.on('exit', function () { console.log('exit'); }); capture-exit-2.0.0/test-natural-exit-subprocess-error.js000066400000000000000000000003171340721404200233660ustar00rootroot00000000000000'use strict'; var capture = require('./'); capture.captureExit(); capture.onExit(function () { console.log('onExit'); }); process.on('exit', function () { console.log('exit'); process.exit(1); }); capture-exit-2.0.0/test-natural-exit-subprocess.js000066400000000000000000000002721340721404200222370ustar00rootroot00000000000000'use strict'; var capture = require('./'); capture.captureExit(); capture.onExit(function () { console.log('onExit'); }) process.on('exit', function () { console.log('exit'); }); capture-exit-2.0.0/test.js000066400000000000000000000334741340721404200154500ustar00rootroot00000000000000var expect = require('chai').expect; var RSVP = require('rsvp'); var originalExit = process.exit; // keep this around for good measure. var exit = require('./'); var childProcess = require('child_process'); var execa = require('execa'); var error = console.error; describe('capture-exit', function() { beforeEach(function() { expect(process.exit, 'ensure we start in a correct state').to.equal(originalExit); }); afterEach(function() { console.error = error; // always restore; // always restore, in case we have bugs in our code while developing exit._reset(); process.exit = originalExit; }); describe('.releaseExit', function() { it('does nothing if no exit has yet been captured', function() { exit.releaseExit(); expect(process.exit, 'ensure we remain in a correct state').to.equal(originalExit); }); it('restores the original exit', function() { exit.captureExit(); expect(process.exit, 'ensure we have captured exit').to.not.equal(originalExit); exit.releaseExit(); expect(process.exit, 'ensure we remain in a correct state').to.equal(originalExit); exit.releaseExit(); expect(process.exit, 'ensure we still remain in a correct state').to.equal(originalExit); }); }); describe('.captureExit', function() { it('replace existing exit', function() { exit.captureExit(); expect(process.exit, 'ensure we have replaced').to.not.equal(originalExit); }); it('replace existing but foreign exit', function() { var differentExit = process.exit = function() { }; exit.captureExit(); expect(process.exit, 'ensure we have replaced').to.not.equal(originalExit); expect(process.exit, 'ensure we have replaced').to.not.equal(differentExit); exit.releaseExit(); expect(process.exit, 'we have correctly restored the right exit').to.equal(differentExit); }); describe('integration', function() { it('works (simply)', function() { var exitWasCalled = 0; var onExitWasCalled = 0; process.exit = function stubExit(code) { exitWasCalled++; expect(code).to.equal('the expected code'); }; var deferred; exit.captureExit(); exit.onExit(function() { onExitWasCalled++; deferred = RSVP.defer(); return deferred.promise; }); process.exit('the expected code'); expect(exitWasCalled).to.equal(0); expect(onExitWasCalled).to.equal(0); return delay(100).then(function() { deferred.resolve(); return deferred.promise.then(function() { expect(onExitWasCalled).to.equal(1); }); }).finally(function() { expect(onExitWasCalled).to.equal(1); }); }); it('works (multiple exits)', function() { var exitWasCalled = 0; var onExitWasCalled = 0; var deferred; var lastDeferred = RSVP.defer(); process.exit = function stubExit(code) { exitWasCalled++; try { expect(code).to.equal('the expected code'); lastDeferred.resolve(); } catch(e) { lastDeferred.reject(e); } }; exit.captureExit(); exit.onExit(function(code) { onExitWasCalled++; deferred = RSVP.defer(); expect(code).to.equal('the expected code'); return deferred.promise; }); process.exit('the expected code'); process.exit('NOT the expected code'); expect(exitWasCalled).to.equal(0); expect(onExitWasCalled).to.equal(0); return delay(100).then(function() { deferred.resolve(); return deferred.promise.then(function() { expect(onExitWasCalled).to.equal(1); }); }).finally(function() { expect(onExitWasCalled).to.equal(1); return lastDeferred.promise; }); }); it('exits with 1 if a prior exit handler throws', function() { var deferred; var lastDeferred = RSVP.defer(); var exitWasCalled = 0; var onExitWasCalled = 0; process.exit = function stubExit(code) { exitWasCalled++; try { expect(code).to.equal(1); lastDeferred.resolve(); } catch(e) { lastDeferred.reject(e); } }; var didConsoleError = 0; var badThingsAreBad = new Error('bad things are bad'); console.error = function(theError) { didConsoleError++; expect(theError).to.equal(badThingsAreBad); }; exit.captureExit(); exit.onExit(function(code) { onExitWasCalled++; deferred = RSVP.defer(); throw badThingsAreBad; }); process.exit('NOT the expected code'); process.exit('NOT the expected code'); expect(exitWasCalled).to.equal(0); expect(onExitWasCalled).to.equal(0); return delay(100).then(function() { expect(didConsoleError).to.eql(1); deferred.resolve(); return deferred.promise.then(function() { expect(onExitWasCalled).to.equal(1); }); }).finally(function() { expect(onExitWasCalled).to.equal(1); return lastDeferred.promise; }).finally(function () { expect(exitWasCalled).to.equal(1); }); }); it('process.exit with an error while natural exit completes', function() { var exitWasCalled = 0; var onExitWasCalled = 0; var exitCode; process.exit = function stubExit(code) { exitWasCalled++; exitCode = code; }; var deferred; exit.captureExit(); exit.onExit(function() { onExitWasCalled++; deferred = RSVP.defer(); return deferred.promise; }); process.exit(); return delay(10).then(function() { expect(onExitWasCalled).to.equal(1); process.exit('the expected code'); process.exit('NOT the expected code'); return delay(10).then(function() { deferred.resolve(); }); }).then(function() { return delay(0).then(function() { expect(onExitWasCalled, 'exit handler invoked').to.equal(1); expect(exitWasCalled, 'real exit was called once').to.equal(1); expect(exitCode, 'called with an expected exit code').to.equal('the expected code'); }); }); }); it('process.exit with an error while natural exit with error code completes', function() { var exitWasCalled = 0; var onExitWasCalled = 0; var finalExitCode; process.exit = function stubExit(code) { exitWasCalled++; finalExitCode = code; }; var deferred; exit.captureExit(); exit.onExit(function() { onExitWasCalled++; deferred = RSVP.defer(); return deferred.promise; }); process.exit('the expected code'); return delay(10).then(function() { expect(onExitWasCalled).to.equal(1); process.exit('an unexpected code'); return delay(10).then(function() { deferred.resolve(); }); }).then(function() { return delay(0).then(function() { expect(onExitWasCalled, 'exit handler invoked').to.equal(1); expect(exitWasCalled, 'real exit was called once').to.equal(1); expect(finalExitCode, 'called with an expected exit code').to.equal('the expected code'); }); }); }); }); }); describe('.onExit', function() { var didExit; function handler(options) { options = options || {}; var code = options.code; var timeout = options.timeout; return function() { // sync if (!timeout) { didExit++; if (code) { return RSVP.Promise.reject(code); } return; } // async if timeout specified return new RSVP.Promise(function(resolve, reject) { setTimeout(function() { didExit++; if (!code) { resolve(); } else { reject(code); } }, timeout); }); } }; beforeEach(function() { didExit = 0; }); it('subscribes', function() { exit.captureExit(); function foo() { didExit++; } exit.onExit(foo); return exit._flush().then(function() { expect(didExit).to.equal(1); didExit = 0; return exit._flush().then(function() { expect(didExit).to.equal(0); }); }); }); it('throws an exit code of the first registered handler which rejects', function() { exit.captureExit(); exit.onExit(handler({ timeout: 10 })); exit.onExit(handler({ code: 400, timeout: 20 })); exit.onExit(handler({ code: 503 })); var resolved = false; return exit._flush() .then(function() { resolved = true; }) .catch(function(reason) { expect(reason, 'fails with the first happened error code').to.equal(503); expect(didExit, '3 exit handlers are called').to.equal(3); }) .finally(function() { expect(resolved, 'should not be resolved').to.equal(false); }); }); it('does not subscribe duplicates', function() { exit.captureExit(); function foo() { didExit++; } exit.onExit(foo); exit.onExit(foo); return exit._flush().then(function() { expect(didExit).to.equal(1); didExit = 0; return exit._flush().then(function() { expect(didExit).to.equal(0); }); }); }); it('throws if exit is not captured', function() { expect(function () { exit.onExit(function () { }); }).to.throw('Cannot install handler when exit is not captured. Call `captureExit()` first'); }); it('throws if an exit is already happening', function() { return new Promise(function (resolve, reject) { process.exit = function doNotReallyExit() { } exit.captureExit(); function addHandler() { try { expect(function () { exit.onExit(function () { console.log("it's too late!"); }); }).to.throw('Cannot install handler while `onExit` handlers are running.'); } catch(e) { reject(e); } resolve(); } exit.onExit(addHandler); process.exit(2); }); }); }); describe('.offExit', function() { it('unsubscribes', function() { exit.captureExit(); var didExit = 0; var didExitBar = 0; function foo() { didExit++; } function bar() { didExitBar++; } exit.onExit(foo); exit.onExit(bar); exit.offExit(foo); return exit._flush().then(function() { expect(didExit).to.equal(0); expect(didExitBar).to.equal(1); }); }); it('does not unsubscribe duplicates', function() { exit.captureExit(); var didExit = 0; var didExitBar = 0; function foo() { didExit++; } function bar() { didExitBar++; } exit.onExit(foo); exit.onExit(bar); exit.offExit(foo); exit.offExit(foo); return exit._flush().then(function() { expect(didExit).to.equal(0); expect(didExitBar).to.equal(1); }); }); }); describe('handlerCount', function() { it('returns the current handler length', function() { exit.captureExit(); expect(exit.listenerCount()).to.equal(0); function foo() {} function bar() {} exit.onExit(foo); expect(exit.listenerCount()).to.equal(1); exit.onExit(bar); expect(exit.listenerCount()).to.equal(2); exit.offExit(foo); expect(exit.listenerCount()).to.equal(1); exit.offExit(bar); expect(exit.listenerCount()).to.equal(0); }); }); }); describe('natural exit', function() { it('runs handlers on a natural exit', function() { var output = childProcess.execSync('node test-natural-exit-subprocess.js'); expect(output+'').to.include('onExit'); expect(output+'').to.include('exit'); }); it("exits with error code if a process.on('exit') handler calls process.exit with code", function() { var succeeded = false; try { var output = childProcess.execSync('node test-natural-exit-subprocess-error.js'); succeeded = true; } catch(e) { expect(e.output+'').to.include('onExit'); expect(e.output+'').to.include('exit'); } if (succeeded) { throw new Error('Unexpected zero exit status for process.exit(1)'); } }); it("exits with error code if a captureExit.onExit handler calls process.exit with code", function() { var succeeded = false; try { var output = childProcess.execSync('node test-natural-exit-subprocess-error-exit-from-captures-on-exit.js'); succeeded = true; } catch(e) { expect(e.output+'').to.include('onExit'); expect(e.output+'').to.include('onExit2'); expect(e.output+'').to.include('exit'); } if (succeeded) { throw new Error('Unexpected zero exit status for process.exit(1)'); } }); it("status code from exit within procces.on('exit') handler trumps prior process.exit's", function() { return execa.shell('node process-exit-during-final-exit-subprocess.js').then(function(a) { expect(true, 'should not fulfill').to.eql(false); }, function(reason) { expect(reason.code).to.eql(1); }); }); }); function delay(milliseconds) { return new RSVP.Promise(function(resolve) { setTimeout(resolve, milliseconds); }); } capture-exit-2.0.0/yarn.lock000066400000000000000000000300721340721404200157450ustar00rootroot00000000000000# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" dependencies: balanced-match "^1.0.0" concat-map "0.0.1" browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" chai@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" dependencies: assertion-error "^1.1.0" check-error "^1.0.2" deep-eql "^3.0.1" get-func-name "^2.0.0" pathval "^1.1.0" type-detect "^4.0.5" chalk@^2.0.1, chalk@^2.3.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" dependencies: restore-cursor "^2.0.0" cli-spinners@^1.1.0: version "1.3.1" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" dependencies: color-name "1.1.3" color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" commander@2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: nice-try "^1.0.4" path-key "^2.0.1" semver "^5.5.0" shebang-command "^1.2.0" which "^1.2.9" debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: ms "2.0.0" deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" dependencies: type-detect "^4.0.0" defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" dependencies: clone "^1.0.2" diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" dependencies: once "^1.4.0" escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" execa@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" dependencies: cross-spawn "^6.0.0" get-stream "^4.0.0" is-stream "^1.1.0" npm-run-path "^2.0.0" p-finally "^1.0.0" signal-exit "^3.0.0" strip-eof "^1.0.0" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" get-func-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" dependencies: pump "^3.0.0" glob@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" dependencies: once "^1.3.0" wrappy "1" inherits@2: version "2.0.3" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: chalk "^2.0.1" mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" mkdirp@0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" mocha@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" dependencies: browser-stdout "1.3.1" commander "2.15.1" debug "3.1.0" diff "3.5.0" escape-string-regexp "1.0.5" glob "7.1.2" growl "1.10.5" he "1.1.1" minimatch "3.0.4" mkdirp "0.5.1" supports-color "5.4.0" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" dependencies: path-key "^2.0.0" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" dependencies: mimic-fn "^1.0.0" ora@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ora/-/ora-3.0.0.tgz#8179e3525b9aafd99242d63cc206fd64732741d0" dependencies: chalk "^2.3.1" cli-cursor "^2.1.0" cli-spinners "^1.1.0" log-symbols "^2.2.0" strip-ansi "^4.0.0" wcwidth "^1.0.1" p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" pathval@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" dependencies: end-of-stream "^1.1.0" once "^1.3.1" restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" dependencies: onetime "^2.0.0" signal-exit "^3.0.2" rsvp@^4.8.4: version "4.8.4" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.4.tgz#b50e6b34583f3dd89329a2f23a8a2be072845911" semver@^5.5.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" dependencies: ansi-regex "^3.0.0" strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" supports-color@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" dependencies: has-flag "^3.0.0" supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" dependencies: has-flag "^3.0.0" type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" dependencies: defaults "^1.0.3" which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: isexe "^2.0.0" wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"