var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./Executor", "@dojo/core/async/Task", "../common/util", "../node/util", "fs", "@dojo/core/lang", "../node/ErrorFormatter", "path", "@theintern/leadfoot/Server", "../ProxiedSession", "../Environment", "../resolveEnvironments", "@theintern/leadfoot/Command", "@dojo/shim/Promise", "../Server", "../Suite", "../RemoteSuite", "istanbul-lib-coverage", "istanbul-lib-instrument", "istanbul-lib-source-maps", "istanbul-lib-hook", "@dojo/shim/global", "@theintern/digdug/BrowserStackTunnel", "@theintern/digdug/SeleniumTunnel", "@theintern/digdug/SauceLabsTunnel", "@theintern/digdug/TestingBotTunnel", "@theintern/digdug/CrossBrowserTestingTunnel", "@theintern/digdug/NullTunnel", "../reporters/Pretty", "../reporters/Runner", "../reporters/Simple", "../reporters/JUnit", "../reporters/Cobertura", "../reporters/JsonCoverage", "../reporters/HtmlCoverage", "../reporters/Lcov", "../reporters/Benchmark"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var Executor_1 = require("./Executor");
    var Task_1 = require("@dojo/core/async/Task");
    var util_1 = require("../common/util");
    var util_2 = require("../node/util");
    var fs_1 = require("fs");
    var lang_1 = require("@dojo/core/lang");
    var ErrorFormatter_1 = require("../node/ErrorFormatter");
    var path_1 = require("path");
    var Server_1 = require("@theintern/leadfoot/Server");
    var ProxiedSession_1 = require("../ProxiedSession");
    var Environment_1 = require("../Environment");
    var resolveEnvironments_1 = require("../resolveEnvironments");
    var Command_1 = require("@theintern/leadfoot/Command");
    var Promise_1 = require("@dojo/shim/Promise");
    var Server_2 = require("../Server");
    var Suite_1 = require("../Suite");
    var RemoteSuite_1 = require("../RemoteSuite");
    var istanbul_lib_coverage_1 = require("istanbul-lib-coverage");
    var istanbul_lib_instrument_1 = require("istanbul-lib-instrument");
    var istanbul_lib_source_maps_1 = require("istanbul-lib-source-maps");
    var istanbul_lib_hook_1 = require("istanbul-lib-hook");
    var global_1 = require("@dojo/shim/global");
    // Dig Dug tunnels
    var BrowserStackTunnel_1 = require("@theintern/digdug/BrowserStackTunnel");
    var SeleniumTunnel_1 = require("@theintern/digdug/SeleniumTunnel");
    var SauceLabsTunnel_1 = require("@theintern/digdug/SauceLabsTunnel");
    var TestingBotTunnel_1 = require("@theintern/digdug/TestingBotTunnel");
    var CrossBrowserTestingTunnel_1 = require("@theintern/digdug/CrossBrowserTestingTunnel");
    var NullTunnel_1 = require("@theintern/digdug/NullTunnel");
    // Reporters
    var Pretty_1 = require("../reporters/Pretty");
    var Runner_1 = require("../reporters/Runner");
    var Simple_1 = require("../reporters/Simple");
    var JUnit_1 = require("../reporters/JUnit");
    var Cobertura_1 = require("../reporters/Cobertura");
    var JsonCoverage_1 = require("../reporters/JsonCoverage");
    var HtmlCoverage_1 = require("../reporters/HtmlCoverage");
    var Lcov_1 = require("../reporters/Lcov");
    var Benchmark_1 = require("../reporters/Benchmark");
    var console = global_1.default.console;
    var process = global_1.default.process;
    var Node = (function (_super) {
        __extends(Node, _super);
        function Node(options) {
            var _this = _super.call(this, {
                basePath: process.cwd() + path_1.sep,
                capabilities: { 'idle-timeout': 60 },
                coverageSources: [],
                connectTimeout: 30000,
                environments: [],
                functionalCoverage: true,
                functionalSuites: [],
                instrumenterOptions: {},
                maxConcurrency: Infinity,
                name: 'node',
                reporters: [],
                runInSync: false,
                serveOnly: false,
                serverPort: 9000,
                serverUrl: 'http://localhost:9000',
                socketPort: 9001,
                tunnel: 'selenium',
                tunnelOptions: { tunnelId: String(Date.now()) }
            }) || this;
            _this._sourceMaps = istanbul_lib_source_maps_1.createSourceMapStore();
            _this._instrumentedMaps = istanbul_lib_source_maps_1.createSourceMapStore();
            _this._errorFormatter = new ErrorFormatter_1.default(_this);
            _this._coverageMap = istanbul_lib_coverage_1.createCoverageMap();
            _this.registerReporter('pretty', Pretty_1.default);
            _this.registerReporter('simple', Simple_1.default);
            _this.registerReporter('runner', Runner_1.default);
            _this.registerReporter('benchmark', Benchmark_1.default);
            _this.registerReporter('junit', JUnit_1.default);
            _this.registerReporter('jsoncoverage', JsonCoverage_1.default);
            _this.registerReporter('htmlcoverage', HtmlCoverage_1.default);
            _this.registerReporter('lcov', Lcov_1.default);
            _this.registerReporter('cobertura', Cobertura_1.default);
            _this.registerTunnel('null', NullTunnel_1.default);
            _this.registerTunnel('selenium', SeleniumTunnel_1.default);
            _this.registerTunnel('saucelabs', SauceLabsTunnel_1.default);
            _this.registerTunnel('browserstack', BrowserStackTunnel_1.default);
            _this.registerTunnel('testingbot', TestingBotTunnel_1.default);
            _this.registerTunnel('cbt', CrossBrowserTestingTunnel_1.default);
            if (options) {
                _this.configure(options);
            }
            // Report uncaught errors
            process.on('unhandledRejection', function (reason, promise) {
                if (!_this._listeners['error'] || _this._listeners['error'].length === 0) {
                    console.warn('Unhandled rejection:', promise);
                }
                reason.message = 'Unhandled rejection: ' + reason.message;
                _this.emit('error', reason);
            });
            process.on('uncaughtException', function (reason) {
                if (!_this._listeners['error'] || _this._listeners['error'].length === 0) {
                    console.warn('Unhandled error:', reason);
                }
                reason.message = 'Uncaught exception: ' + reason.message;
                _this.emit('error', reason);
            });
            _this.on('coverage', function (message) {
                _this._coverageMap.merge(message.coverage);
            });
            return _this;
        }
        Object.defineProperty(Node.prototype, "coverageMap", {
            get: function () {
                return this._coverageMap;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Node.prototype, "environment", {
            get: function () {
                return 'node';
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Node.prototype, "instrumentedMapStore", {
            get: function () {
                return this._instrumentedMaps;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Node.prototype, "sourceMapStore", {
            get: function () {
                return this._sourceMaps;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(Node.prototype, "suites", {
            /**
             * The root suites managed by this executor
             */
            get: function () {
                if (this._sessionSuites) {
                    return this._sessionSuites.concat([this._rootSuite]);
                }
                return [this._rootSuite];
            },
            enumerable: true,
            configurable: true
        });
        /**
         * Override Executor#addSuite to handle functional suites
         */
        Node.prototype.addSuite = function (factory) {
            if (this._loadingFunctionalSuites) {
                this._sessionSuites.forEach(factory);
            }
            else {
                _super.prototype.addSuite.call(this, factory);
            }
        };
        /**
         * Retrieve a registered tunnel constructor
         */
        Node.prototype.getTunnel = function (name) {
            return this.getPlugin("tunnel." + name);
        };
        /**
         * Insert coverage instrumentation into a given code string
         */
        Node.prototype.instrumentCode = function (code, filename) {
            this.log('Instrumenting', filename);
            var sourceMap = util_2.readSourceMap(filename, code);
            if (sourceMap) {
                this._sourceMaps.registerMap(filename, sourceMap);
            }
            try {
                var newCode = this._instrumenter.instrumentSync(code, path_1.normalize(filename), sourceMap);
                this._instrumentedMaps.registerMap(filename, this._instrumenter.lastSourceMap());
                return newCode;
            }
            catch (error) {
                this.emit('warning', "Error instrumenting " + filename + ": " + error.message);
                return code;
            }
        };
        /**
         * Load scripts using Node's require
         */
        Node.prototype.loadScript = function (script) {
            if (!Array.isArray(script)) {
                script = [script];
            }
            try {
                script.forEach(function (script) {
                    require(path_1.resolve(script));
                });
            }
            catch (error) {
                return Task_1.default.reject(error);
            }
            return Task_1.default.resolve();
        };
        /**
         * Register a tunnel constructor with the plugin system. It can be retrieved later with getTunnel or getPlugin.
         */
        Node.prototype.registerTunnel = function (name, Ctor) {
            this.registerPlugin('tunnel', name, function () { return Ctor; });
        };
        /**
         * Return true if a given file should be instrumented based on the current config
         */
        Node.prototype.shouldInstrumentFile = function (filename) {
            var excludeInstrumentation = this.config.excludeInstrumentation;
            if (excludeInstrumentation === true) {
                return false;
            }
            var basePath = this._instrumentBasePath;
            filename = util_2.normalizePath(filename);
            return filename.indexOf(basePath) === 0 && !excludeInstrumentation.test(filename.slice(basePath.length));
        };
        Node.prototype._afterRun = function () {
            var _this = this;
            return _super.prototype._afterRun.call(this)
                .finally(function () {
                _this._removeInstrumentationHooks();
                var promises = [];
                if (_this.server) {
                    promises.push(_this.server.stop().then(function () { return _this.emit('serverEnd', _this.server); }));
                }
                if (_this.tunnel) {
                    promises.push(_this.tunnel.stop().then(function () { return _this.emit('tunnelStop', { tunnel: _this.tunnel }); }));
                }
                // We do not want to actually return an array of values, so chain a callback that resolves to undefined
                return Promise_1.default.all(promises).then(function () { }, function (error) { return _this.emit('error', error); });
            });
        };
        Node.prototype._beforeRun = function () {
            var _this = this;
            return _super.prototype._beforeRun.call(this).then(function () {
                var config = _this.config;
                var suite = _this._rootSuite;
                suite.grep = config.grep;
                suite.timeout = config.defaultTimeout;
                suite.bail = config.bail;
                if (config.environments.length > 0 && (config.functionalSuites.length + config.suites.length + config.browser.suites.length > 0) ||
                    // User can start the server without planning to run functional tests
                    config.serveOnly) {
                    var serverTask = new Task_1.default(function (resolve, reject) {
                        var server = new Server_2.default({
                            basePath: config.basePath,
                            executor: _this,
                            port: config.serverPort,
                            runInSync: config.runInSync,
                            socketPort: config.socketPort
                        });
                        server.start()
                            .then(function () {
                            _this.server = server;
                            return _this.emit('serverStart', server);
                        })
                            .then(resolve, reject);
                    });
                    // If we're in serveOnly mode, just start the server server. Don't create session suites or start a tunnel.
                    if (config.serveOnly) {
                        return serverTask.then(function () {
                            // In serveOnly mode we just start the server to static file serving and instrumentation. Return
                            // an unresolved Task to pause indefinitely until canceled.
                            return new Task_1.default(function (resolve) {
                                process.on('SIGINT', function () {
                                    resolve(true);
                                });
                            });
                        });
                    }
                    return serverTask
                        .then(function () {
                        if (config.tunnel === 'browserstack') {
                            var options = config.tunnelOptions;
                            options.servers = options.servers || [];
                            options.servers.push(config.serverUrl);
                        }
                        var TunnelConstructor = _this.getTunnel(config.tunnel);
                        var tunnel = _this.tunnel = new TunnelConstructor(_this.config.tunnelOptions);
                        tunnel.on('downloadprogress', function (progress) {
                            _this.emit('tunnelDownloadProgress', { tunnel: tunnel, progress: progress });
                        });
                        tunnel.on('status', function (status) {
                            _this.emit('tunnelStatus', { tunnel: tunnel, status: status.status });
                        });
                        config.capabilities = lang_1.deepMixin(tunnel.extraCapabilities, config.capabilities);
                        return _this._createSessionSuites().then(function () {
                            return tunnel.start().then(function () { return _this.emit('tunnelStart', { tunnel: tunnel }); });
                        });
                    })
                        .then(function () {
                        return false;
                    });
                }
                return false;
            });
        };
        /**
         * Creates suites for each environment in which tests will be executed. This method will only be called if there are
         * both environments and suites to run.
         */
        Node.prototype._createSessionSuites = function () {
            var _this = this;
            var tunnel = this.tunnel;
            var config = this.config;
            var leadfootServer = new Server_1.default(tunnel.clientUrl, {
                proxy: tunnel.proxy
            });
            var executor = this;
            // Create a subclass of ProxiedSession here that will ensure the executor is set
            var InitializedProxiedSession = (function (_super) {
                __extends(InitializedProxiedSession, _super);
                function InitializedProxiedSession() {
                    var _this = _super !== null && _super.apply(this, arguments) || this;
                    _this.executor = executor;
                    _this.coverageEnabled = config.functionalCoverage && config.excludeInstrumentation !== true;
                    _this.coverageVariable = config.coverageVariable;
                    _this.serverUrl = config.serverUrl;
                    _this.serverBasePathLength = config.basePath.length;
                    return _this;
                }
                return InitializedProxiedSession;
            }(ProxiedSession_1.default));
            leadfootServer.sessionConstructor = InitializedProxiedSession;
            return tunnel.getEnvironments().then(function (tunnelEnvironments) {
                _this._sessionSuites = resolveEnvironments_1.default(config.capabilities, config.environments, tunnelEnvironments).map(function (environmentType) {
                    var session;
                    // Create a new root suite for each environment
                    var suite = new Suite_1.default({
                        name: String(environmentType),
                        publishAfterSetup: true,
                        grep: config.grep,
                        bail: config.bail,
                        tests: [],
                        timeout: config.defaultTimeout,
                        executor: _this,
                        before: function () {
                            var _this = this;
                            executor.log('Creating session for', environmentType);
                            return leadfootServer.createSession(environmentType).then(function (_session) {
                                session = _session;
                                _this.executor.log('Created session:', session.capabilities);
                                var remote = new Command_1.default(session);
                                remote.environmentType = new Environment_1.default(session.capabilities);
                                _this.remote = remote;
                                _this.sessionId = remote.session.sessionId;
                                // Update the name with details from the remote environment
                                _this.name = remote.environmentType.toString();
                            });
                        },
                        after: function () {
                            var _this = this;
                            var remote = this.remote;
                            if (remote) {
                                var endSession = function () {
                                    // Check for an error in this suite or a sub-suite. This check is a bit more
                                    // involved than just checking for a local suite error or failed tests since
                                    // sub-suites may have failures that don't result in failed tests.
                                    function hasError(suite) {
                                        if (suite.error != null || suite.numFailedTests > 0) {
                                            return true;
                                        }
                                        return suite.tests.filter(Suite_1.isSuite).some(hasError);
                                    }
                                    return tunnel.sendJobState(remote.session.sessionId, { success: !hasError(_this) });
                                };
                                if (config.leaveRemoteOpen === true ||
                                    (config.leaveRemoteOpen === 'fail' && this.numFailedTests > 0)) {
                                    return endSession();
                                }
                                return remote.quit().finally(endSession);
                            }
                        }
                    });
                    // If browser-compatible unit tests were added to this executor, add a RemoteSuite to the session suite.
                    // The RemoteSuite will run the suites listed in config.suites and config.browser.suites.
                    if (config.suites.length + config.browser.suites.length > 0) {
                        suite.add(new RemoteSuite_1.default({
                            before: function () {
                                session.coverageEnabled = config.excludeInstrumentation !== true;
                            }
                        }));
                    }
                    return suite;
                });
            });
        };
        /**
         * Load functional test suites
         */
        Node.prototype._loadFunctionalSuites = function () {
            var _this = this;
            this._loadingFunctionalSuites = true;
            var suites = this.config.functionalSuites;
            return Task_1.default.resolve(this._loader(suites))
                .then(function () { _this.log('Loaded functional suites:', suites); })
                .finally(function () { _this._loadingFunctionalSuites = false; });
        };
        Node.prototype._processOption = function (name, value, addToExisting) {
            switch (name) {
                case 'serverUrl':
                    this._setOption(name, util_1.parseValue(name, value, 'string'));
                    break;
                case 'capabilities':
                case 'instrumenterOptions':
                case 'tunnelOptions':
                    this._setOption(name, util_1.parseValue(name, value, 'object'));
                    break;
                // Must be a string, object, or array of (string | object)
                case 'environments':
                    if (!value) {
                        value = [];
                    }
                    else if (!Array.isArray(value)) {
                        value = [value];
                    }
                    value = value.map(function (val) {
                        if (typeof val === 'object' && val.browserName == null) {
                            val.browserName = val.browser;
                        }
                        return val;
                    });
                    this._setOption(name, util_1.parseValue(name, value, 'object[]', 'browserName'), addToExisting);
                    break;
                case 'tunnel':
                    this._setOption(name, util_1.parseValue(name, value, 'string'));
                    break;
                case 'functionalCoverage':
                case 'leaveRemoteOpen':
                case 'serveOnly':
                case 'runInSync':
                    this._setOption(name, util_1.parseValue(name, value, 'boolean'));
                    break;
                case 'coverageSources':
                case 'functionalSuites':
                    this._setOption(name, util_1.parseValue(name, value, 'string[]'), addToExisting);
                    break;
                case 'connectTimeout':
                case 'maxConcurrency':
                case 'serverPort':
                case 'socketPort':
                    this._setOption(name, util_1.parseValue(name, value, 'number'));
                    break;
                default:
                    _super.prototype._processOption.call(this, name, value, addToExisting);
                    break;
            }
        };
        Node.prototype._resolveConfig = function () {
            var _this = this;
            return _super.prototype._resolveConfig.call(this).then(function () {
                var config = _this.config;
                if (!config.internPath) {
                    config.internPath = path_1.dirname(path_1.dirname(__dirname));
                }
                config.internPath = util_2.normalizePath("" + path_1.relative(process.cwd(), config.internPath) + path_1.sep);
                if (/^\.\.\//.test(config.internPath)) {
                    throw new Error("Invalid internPath \"" + config.internPath + "\". If the intern package is symlinked, "
                        + 'config.internPath must be set manually.');
                }
                if (config.benchmarkConfig) {
                    config.reporters.push({
                        name: 'benchmark',
                        options: config.benchmarkConfig
                    });
                }
                _this._instrumentBasePath = util_2.normalizePath("" + path_1.resolve(config.basePath || '') + path_1.sep);
                config.serverUrl = config.serverUrl.replace(/\/*$/, '/');
                if (!config.capabilities.name) {
                    config.capabilities.name = 'intern';
                }
                var buildId = process.env.TRAVIS_COMMIT || process.env.BUILD_TAG;
                if (buildId) {
                    config.capabilities.build = buildId;
                }
                // Expand suite globs
                config.suites = util_2.expandFiles(config.suites);
                config.functionalSuites = util_2.expandFiles(config.functionalSuites);
                // Expand suite globs in node, browser objects
                config.node.suites = util_2.expandFiles(config.node.suites);
                config.browser.suites = util_2.expandFiles(config.browser.suites);
                // Install the instrumenter in resolve config so it will be able to handle suites
                _this._instrumenter = istanbul_lib_instrument_1.createInstrumenter(lang_1.mixin({}, __assign({ coverageVariable: config.coverageVariable }, config.instrumenterOptions), {
                    preserveComments: true,
                    produceSourceMap: true
                }));
                if (config.excludeInstrumentation !== true) {
                    _this._setInstrumentationHooks();
                }
            });
        };
        Node.prototype._runTests = function () {
            var _this = this;
            var testTask;
            return new Task_1.default(function (resolve, reject) {
                _super.prototype._runTests.call(_this)
                    .then(function () {
                    if (!_this._sessionSuites) {
                        return;
                    }
                    return _this._loadFunctionalSuites()
                        .then(function () { return testTask = _this._runRemoteTests(); });
                })
                    .then(resolve, reject);
            }, function () {
                if (testTask && testTask.state === 1 /* Pending */) {
                    testTask.cancel();
                }
            });
        };
        Node.prototype._runRemoteTests = function () {
            var _this = this;
            var config = this.config;
            var sessionSuites = this._sessionSuites;
            var queue = new FunctionQueue(config.maxConcurrency || Infinity);
            this.log('Running', sessionSuites.length, 'suites with maxConcurrency', config.maxConcurrency);
            var runTask = new Task_1.default(function (resolve, reject) {
                Task_1.default.all(sessionSuites.map(function (suite) {
                    _this.log('Queueing suite', suite.name);
                    return queue.enqueue(function () {
                        _this.log('Running suite', suite.name);
                        return suite.run();
                    });
                })).then(resolve, reject);
            }, function () {
                _this.log('Canceling remote tests');
                queue.clear();
            });
            return runTask
                .then(function () { })
                .finally(function () {
                if (config.functionalCoverage !== false) {
                    // Collect any local coverage generated by functional tests
                    _this.log('Emitting coverage');
                    return _this._emitCoverage('functional tests');
                }
            })
                .finally(function () {
                // If coverageSources is set, generate initial coverage data for files with no coverage results
                var filesWithCoverage = _this._coverageMap.files();
                util_2.expandFiles(_this.config.coverageSources)
                    .map(function (path) { return path_1.resolve(path); })
                    .filter(function (path) { return filesWithCoverage.indexOf(path) === -1; })
                    .forEach(function (filename) {
                    var code = fs_1.readFileSync(filename, { encoding: 'utf8' });
                    var instrumentedCode = _this.instrumentCode(code, filename);
                    var coverage = istanbul_lib_instrument_1.readInitialCoverage(instrumentedCode);
                    _this._coverageMap.addFileCoverage(coverage.coverageData);
                });
            });
        };
        /**
         * Adds hooks for code coverage instrumentation in the Node.js loader.
         */
        Node.prototype._setInstrumentationHooks = function () {
            var _this = this;
            istanbul_lib_hook_1.hookRunInThisContext(function (filename) { return _this.shouldInstrumentFile(filename); }, function (code, filename) { return _this.instrumentCode(code, filename); });
            this._unhookRequire = istanbul_lib_hook_1.hookRequire(function (filename) { return _this.shouldInstrumentFile(filename); }, function (code, filename) { return _this.instrumentCode(code, filename); });
        };
        /**
         * Removes instrumentation hooks
         */
        Node.prototype._removeInstrumentationHooks = function () {
            istanbul_lib_hook_1.unhookRunInThisContext();
            if (this._unhookRequire) {
                this._unhookRequire();
                this._unhookRequire = null;
            }
        };
        return Node;
    }(Executor_1.default));
    exports.default = Node;
    /**
     * A basic FIFO function queue to limit the number of currently executing asynchronous functions.
     */
    var FunctionQueue = (function () {
        function FunctionQueue(maxConcurrency) {
            this.maxConcurrency = maxConcurrency;
            this.queue = [];
            this.activeTasks = [];
            this.funcTasks = [];
        }
        FunctionQueue.prototype.enqueue = function (func) {
            var _this = this;
            var funcTask = new Task_1.default(function (resolve, reject) {
                _this.queue.push({ func: func, resolve: resolve, reject: reject });
            });
            this.funcTasks.push(funcTask);
            if (this.activeTasks.length < this.maxConcurrency) {
                this.next();
            }
            return funcTask;
        };
        FunctionQueue.prototype.clear = function () {
            this.activeTasks.forEach(function (task) { return task.cancel(); });
            this.funcTasks.forEach(function (task) { return task.cancel(); });
            this.activeTasks = [];
            this.funcTasks = [];
            this.queue = [];
        };
        FunctionQueue.prototype.next = function () {
            var _this = this;
            if (this.queue.length > 0) {
                var _a = this.queue.shift(), func = _a.func, resolve_1 = _a.resolve, reject = _a.reject;
                var task_1 = func().then(resolve_1, reject).finally(function () {
                    // Remove the task from the active task list and kick off the next task
                    util_1.pullFromArray(_this.activeTasks, task_1);
                    _this.next();
                });
                this.activeTasks.push(task_1);
            }
        };
        return FunctionQueue;
    }());
});
//# sourceMappingURL=Node.js.map