"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFormattedMilliseconds = exports.getFormattedDateComponent = exports.getFixedLengthDateString = exports.stringify = exports.getWinRegPropertyValue = exports.getValueFromNestedObject = exports.getPidFromiOSSimulatorLogs = exports.getProjectFilesConfig = exports.connectEventuallyUntilTimeout = exports.getHash = exports.connectEventually = exports.attachAwaitDetach = exports.isPromise = exports.hook = exports.decorateMethod = exports.appendZeroesToVersion = exports.getFuturesResults = exports.parseJson = exports.trimSymbol = exports.remove = exports.getMessageWithBorders = exports.createTable = exports.sleep = exports.getCurrentEpochTime = exports.isNullOrWhitespace = exports.fromWindowsRelativePathToUnix = exports.isNumberWithoutExponent = exports.block = exports.toBoolean = exports.isInteractive = exports.setIsInteractive = exports.getRelativeToRootPath = exports.formatListOfNames = exports.isResponseRedirect = exports.isRequestSuccessful = exports.stringReplaceAll = exports.createGUID = exports.quoteString = exports.getPropertyName = exports.settlePromises = exports.deferPromise = exports.executeActionByChunks = exports.getShortPluginName = exports.regExpEscape = exports.createRegExp = exports.isInstallingNativeScriptGlobally = exports.getCurrentNpmCommandArgv = exports.someWithRegExps = exports.doesCurrentNpmCommandMatch = exports.stripComments = void 0;
exports.hasValidAndroidSigning = exports.annotate = void 0;
const uuid_1 = require("uuid");
const os_1 = require("os");
const constants_1 = require("./constants");
const crypto = require("crypto");
const _ = require("lodash");
const Table = require("cli-table3");
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
function stripComments(content) {
    const newContent = content.replace(STRIP_COMMENTS, "");
    return newContent;
}
exports.stripComments = stripComments;
function doesCurrentNpmCommandMatch(patterns) {
    const currentNpmCommandArgv = getCurrentNpmCommandArgv();
    let result = false;
    if (currentNpmCommandArgv.length) {
        result = someWithRegExps(currentNpmCommandArgv, patterns);
    }
    return result;
}
exports.doesCurrentNpmCommandMatch = doesCurrentNpmCommandMatch;
/**
 * Equivalent of lodash's some, but instead of lambda, just pass array of Regular Expressions.
 * If any of them matches any of the given elements, true is returned.
 * @param {string[]} array Elements to be checked.
 * @param {RegExp[]} patterns Regular expressions to be tested
 * @returns {boolean} True in case any element of the array matches any of the patterns. False otherwise.
 */
function someWithRegExps(array, patterns) {
    return _.some(array, (item) => _.some(patterns, (pattern) => !!item.match(pattern)));
}
exports.someWithRegExps = someWithRegExps;
function getCurrentNpmCommandArgv() {
    let result = [];
    if (process.env && process.env.npm_config_argv) {
        try {
            const npmConfigArgv = JSON.parse(process.env.npm_config_argv);
            result = npmConfigArgv.original || [];
        }
        catch (error) {
            // ignore
        }
    }
    return result;
}
exports.getCurrentNpmCommandArgv = getCurrentNpmCommandArgv;
function isInstallingNativeScriptGlobally() {
    return (isInstallingNativeScriptGloballyWithNpm() ||
        isInstallingNativeScriptGloballyWithYarn());
}
exports.isInstallingNativeScriptGlobally = isInstallingNativeScriptGlobally;
function isInstallingNativeScriptGloballyWithNpm() {
    const isInstallCommand = doesCurrentNpmCommandMatch([/^install$/, /^i$/]);
    const isGlobalCommand = doesCurrentNpmCommandMatch([/^--global$/, /^-g$/]);
    const hasNativeScriptPackage = doesCurrentNpmCommandMatch([
        /^nativescript(@.*)?$/,
        /nativescript-(.*)\.tgz?$/,
    ]);
    return isInstallCommand && isGlobalCommand && hasNativeScriptPackage;
}
function isInstallingNativeScriptGloballyWithYarn() {
    // yarn populates the same env used by npm - npm_config_argv, so check it for yarn specific command
    const isInstallCommand = doesCurrentNpmCommandMatch([/^add$/]);
    const isGlobalCommand = doesCurrentNpmCommandMatch([/^global$/]);
    const hasNativeScriptPackage = doesCurrentNpmCommandMatch([
        /^nativescript(@.*)?$/,
        /nativescript-(.*)\.tgz?$/,
    ]);
    return isInstallCommand && isGlobalCommand && hasNativeScriptPackage;
}
/**
 * Creates regular expression from input string.
 * The method replaces all occurences of RegExp special symbols in the input string with \<symbol>.
 * @param {string} input The string from which a regular expression should be created.
 * @param {string} opts RegExp options, for example "gm" - global and multiline.
 * @returns {RegExp} The regular expression created from the input string.
 */
function createRegExp(input, opts) {
    if (!input || !_.isString(input)) {
        throw new Error("Input must be a string.");
    }
    const escapedSource = regExpEscape(input);
    return new RegExp(escapedSource, opts);
}
exports.createRegExp = createRegExp;
/**
 * Escapes all special symbols used in regex.
 * @param {string} input The string in which to replace the special regexp symbols.
 * @returns {string} A string in which all regex symbols are escaped.
 */
function regExpEscape(input) {
    // https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
    return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
exports.regExpEscape = regExpEscape;
function getShortPluginName(pluginName) {
    return sanitizePluginName(pluginName).replace(/[\-]/g, "_");
}
exports.getShortPluginName = getShortPluginName;
function sanitizePluginName(pluginName) {
    // avoid long plugin names, exclude the npm module scope (@scope/nativescript-plugin) from the android plugin name
    return pluginName.split("/").pop();
}
function executeActionByChunks(initialData, chunkSize, elementAction) {
    return __awaiter(this, void 0, void 0, function* () {
        let arrayToChunk;
        let action;
        if (_.isArray(initialData)) {
            arrayToChunk = initialData;
            action = (element) => elementAction(element, initialData.indexOf(element));
        }
        else {
            arrayToChunk = _.keys(initialData);
            action = (key) => elementAction(initialData[key], key);
        }
        const chunks = _.chunk(arrayToChunk, chunkSize);
        for (const chunk of chunks) {
            yield Promise.all(_.map(chunk, (element) => action(element)));
        }
    });
}
exports.executeActionByChunks = executeActionByChunks;
function deferPromise() {
    let resolve;
    let reject;
    let isResolved = false;
    let isRejected = false;
    let promise;
    let result;
    promise = new Promise((innerResolve, innerReject) => {
        resolve = (value) => {
            isResolved = true;
            result = value;
            return innerResolve(value);
        };
        reject = (reason) => {
            isRejected = true;
            return innerReject(reason);
        };
    });
    return {
        promise,
        resolve,
        reject,
        isResolved: () => isResolved,
        isRejected: () => isRejected,
        isPending: () => !isResolved && !isRejected,
        getResult: () => result,
    };
}
exports.deferPromise = deferPromise;
/**
 * Executes all promises and does not stop in case any of them throws.
 * Returns the results of all promises in array when all are successfully resolved.
 * In case any of the promises is rejected, rejects the resulted promise with all accumulated errors.
 * @param {Promise<T>[]} promises Promises to be resolved.
 * @returns {Promise<T[]>} New promise which will be resolved with the results of all promises.
 */
function settlePromises(promises) {
    return new Promise((resolve, reject) => {
        let settledPromisesCount = 0;
        const results = [];
        const errors = [];
        const length = promises.length;
        if (!promises.length) {
            resolve(null);
        }
        _.forEach(promises, (currentPromise, index) => {
            currentPromise
                .then((result) => {
                results[index] = result;
            })
                .catch((err) => {
                // Accumulate all errors.
                errors.push(err);
            })
                .then(() => {
                settledPromisesCount++;
                if (settledPromisesCount === length) {
                    errors.length
                        ? reject(new Error(`Multiple errors were thrown:${os_1.EOL}${errors
                            .map((e) => e.message || e)
                            .join(os_1.EOL)}`))
                        : resolve(results);
                }
            })
                .catch();
        });
    });
}
exports.settlePromises = settlePromises;
function getPropertyName(func) {
    if (func) {
        const match = func
            .toString()
            .match(/(?:return\s+?.*\.(.+);)|(?:=>\s*?.*\.(.+)\b)/);
        if (match) {
            return (match[1] || match[2]).trim();
        }
    }
    return null;
}
exports.getPropertyName = getPropertyName;
function bashQuote(s) {
    if (s[0] === "'" && s[s.length - 1] === "'") {
        return s;
    }
    // replace ' with '"'"' and wrap in ''
    return "'" + s.replace(/'/g, "'\"'\"'") + "'";
}
function cmdQuote(s) {
    if (s[0] === '"' && s[s.length - 1] === '"') {
        return s;
    }
    // replace " with \" and wrap in ""
    return '"' + s.replace(/"/g, '\\"') + '"';
}
function quoteString(s) {
    if (!s) {
        return s;
    }
    return (0, os_1.platform)() === "win32" ? cmdQuote(s) : bashQuote(s);
}
exports.quoteString = quoteString;
function createGUID(useBraces) {
    let output;
    useBraces = useBraces === undefined ? true : useBraces;
    if (useBraces) {
        output = "{" + (0, uuid_1.v4)() + "}";
    }
    else {
        output = (0, uuid_1.v4)();
    }
    return output;
}
exports.createGUID = createGUID;
function stringReplaceAll(inputString, find, replace) {
    return inputString.split(find).join(replace);
}
exports.stringReplaceAll = stringReplaceAll;
function isRequestSuccessful(request) {
    return request.statusCode >= 200 && request.statusCode < 300;
}
exports.isRequestSuccessful = isRequestSuccessful;
function isResponseRedirect(response) {
    return _.includes([301, 302, 303, 307, 308], response.statusCode);
}
exports.isResponseRedirect = isResponseRedirect;
function formatListOfNames(names, conjunction) {
    conjunction = conjunction === undefined ? "or" : conjunction;
    if (names.length <= 1) {
        return names[0];
    }
    else {
        return (_.initial(names).join(", ") +
            " " +
            conjunction +
            " " +
            names[names.length - 1]);
    }
}
exports.formatListOfNames = formatListOfNames;
function getRelativeToRootPath(rootPath, filePath) {
    const relativeToRootPath = filePath.substr(rootPath.length);
    return relativeToRootPath;
}
exports.getRelativeToRootPath = getRelativeToRootPath;
let customIsInteractive;
function setIsInteractive(override) {
    customIsInteractive = override;
}
exports.setIsInteractive = setIsInteractive;
function isInteractive() {
    if (customIsInteractive) {
        return customIsInteractive();
    }
    const result = isRunningInTTY() && !isCIEnvironment();
    return result;
}
exports.isInteractive = isInteractive;
/**
 * Checks if current process is running in Text Terminal (TTY)
 */
function isRunningInTTY() {
    return (process.stdout &&
        process.stdout.isTTY &&
        process.stdin &&
        process.stdin.isTTY);
}
function isCIEnvironment() {
    // The following CI environments set their own environment variables that we respect:
    //  travis: "CI",
    //  circleCI: "CI",
    //  jenkins: "JENKINS_HOME"
    return !!(process.env && (process.env.CI || process.env.JENKINS_HOME));
}
function toBoolean(str) {
    return !!(str && str.toString && str.toString().toLowerCase() === "true");
}
exports.toBoolean = toBoolean;
function block(operation) {
    if (isInteractive()) {
        process.stdin.setRawMode(false);
    }
    operation();
    if (isInteractive()) {
        process.stdin.setRawMode(true);
    }
}
exports.block = block;
function isNumberWithoutExponent(n) {
    const parsedNum = parseFloat(n);
    return (!isNaN(parsedNum) &&
        isFinite(n) &&
        n.toString &&
        n.toString() === parsedNum.toString());
}
exports.isNumberWithoutExponent = isNumberWithoutExponent;
function fromWindowsRelativePathToUnix(windowsRelativePath) {
    return windowsRelativePath.replace(/\\/g, "/");
}
exports.fromWindowsRelativePathToUnix = fromWindowsRelativePathToUnix;
function isNullOrWhitespace(input) {
    if (!input && input !== false) {
        return true;
    }
    return _.isString(input) && input.replace(/\s/gi, "").length < 1;
}
exports.isNullOrWhitespace = isNullOrWhitespace;
function getCurrentEpochTime() {
    const dateTime = new Date();
    return dateTime.getTime();
}
exports.getCurrentEpochTime = getCurrentEpochTime;
function sleep(ms) {
    return __awaiter(this, void 0, void 0, function* () {
        return new Promise((resolve, reject) => {
            setTimeout(() => __awaiter(this, void 0, void 0, function* () { return resolve(); }), ms);
        });
    });
}
exports.sleep = sleep;
function createTable(headers, data) {
    const table = new Table({
        head: headers,
        chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
    });
    _.forEach(data, (row) => table.push(row));
    return table;
}
exports.createTable = createTable;
function getMessageWithBorders(message, spanLength = 3) {
    if (!message) {
        return "";
    }
    const longestRowLength = message.split("\n").sort((a, b) => {
        return b.length - a.length;
    })[0].length;
    let border = "*".repeat(longestRowLength + 2 * spanLength); // * 2 for both sides
    if (border.length % 2 === 0) {
        border += "*"; // the * should always be an odd number in order to get * in each edge (we will remove the even *s below)
    }
    border = border.replace(/\*\*/g, "* "); // ***** => * * * in order to have similar padding to the side borders
    const formatRow = function (row) {
        return (_.padEnd("*", spanLength) +
            _.padEnd(row, border.length - 2 * spanLength) +
            _.padStart("*", spanLength) +
            os_1.EOL);
    };
    const emptyRow = formatRow("");
    const messageWithBorders = [];
    messageWithBorders.push(os_1.EOL, border + os_1.EOL, emptyRow, ...message.split("\n").map((row) => formatRow(row.trim())), emptyRow, border + os_1.EOL, os_1.EOL);
    return messageWithBorders.join("");
}
exports.getMessageWithBorders = getMessageWithBorders;
function remove(array, predicate, numberOfElements) {
    numberOfElements = numberOfElements || 1;
    const index = _.findIndex(array, predicate);
    if (index === -1) {
        return new Array();
    }
    return array.splice(index, numberOfElements);
}
exports.remove = remove;
function trimSymbol(str, symbol) {
    while (str.charAt(0) === symbol) {
        str = str.substr(1);
    }
    while (str.charAt(str.length - 1) === symbol) {
        str = str.substr(0, str.length - 1);
    }
    return str;
}
exports.trimSymbol = trimSymbol;
function parseJson(data) {
    // Replace BOM from the header of the file if it exists
    return JSON.parse(data.replace(/^\uFEFF/, ""));
}
exports.parseJson = parseJson;
// TODO: Use generic for predicatе predicate: (element: T|T[]) when TypeScript support this.
function getFuturesResults(promises, predicate) {
    return __awaiter(this, void 0, void 0, function* () {
        const results = yield Promise.all(promises);
        return _(results).filter(predicate).flatten().value();
    });
}
exports.getFuturesResults = getFuturesResults;
/**
 * Appends zeroes to a version string until it reaches a specified length.
 * @param {string} version The version on which to append zeroes.
 * @param requiredVersionLength The required length of the version string.
 * @returns {string} Appended version string. In case input is null, undefined or empty string, it is returned immediately without appending anything.
 */
function appendZeroesToVersion(version, requiredVersionLength) {
    if (version) {
        const zeroesToAppend = requiredVersionLength - version.split(".").length;
        for (let index = 0; index < zeroesToAppend; index++) {
            version += ".0";
        }
    }
    return version;
}
exports.appendZeroesToVersion = appendZeroesToVersion;
function decorateMethod(before, after) {
    return (target, propertyKey, descriptor) => {
        const sink = descriptor.value;
        descriptor.value = function (...args) {
            return __awaiter(this, void 0, void 0, function* () {
                let newMethods = null;
                if (before) {
                    newMethods = yield before(sink, this, args);
                }
                let hasBeenReplaced = false;
                let result;
                if (newMethods && newMethods.length) {
                    const replacementMethods = _.filter(newMethods, (f) => _.isFunction(f));
                    if (replacementMethods.length > 0) {
                        hasBeenReplaced = true;
                        const chainedReplacementMethod = _.reduce(replacementMethods, (prev, next) => next.bind(next, args, prev), sink.bind(this));
                        result = chainedReplacementMethod();
                    }
                }
                if (!hasBeenReplaced) {
                    result = sink.apply(this, args);
                }
                if (after) {
                    return yield after(sink, this, result, args);
                }
                return result;
            });
        };
    };
}
exports.decorateMethod = decorateMethod;
function hook(commandName) {
    function getHooksService(self) {
        let hooksService = self.$hooksService;
        if (!hooksService) {
            const injector = self.$injector;
            if (!injector) {
                throw Error("Type with hooks needs to have either $hooksService or $injector injected.");
            }
            hooksService = injector.resolve("hooksService");
        }
        return hooksService;
    }
    function prepareArguments(method, args, hooksService) {
        annotate(method);
        const argHash = {};
        for (let i = 0; i < method.$inject.args.length; ++i) {
            argHash[method.$inject.args[i]] = args[i];
        }
        argHash.$arguments = args;
        const result = {};
        result[hooksService.hookArgsName] = argHash;
        return result;
    }
    return decorateMethod((method, self, args) => __awaiter(this, void 0, void 0, function* () {
        const hooksService = getHooksService(self);
        return hooksService.executeBeforeHooks(commandName, prepareArguments(method, args, hooksService));
    }), (method, self, resultPromise, args) => __awaiter(this, void 0, void 0, function* () {
        const result = yield resultPromise;
        const hooksService = getHooksService(self);
        yield hooksService.executeAfterHooks(commandName, prepareArguments(method, args, hooksService));
        return Promise.resolve(result);
    }));
}
exports.hook = hook;
function isPromise(candidateFuture) {
    return !!(candidateFuture && typeof candidateFuture.then === "function");
}
exports.isPromise = isPromise;
function attachAwaitDetach(eventName, eventEmitter, eventHandler, operation) {
    return __awaiter(this, void 0, void 0, function* () {
        eventEmitter.on(eventName, eventHandler);
        try {
            yield operation;
        }
        finally {
            eventEmitter.removeListener(eventName, eventHandler);
        }
    });
}
exports.attachAwaitDetach = attachAwaitDetach;
function connectEventually(factory, handler) {
    return __awaiter(this, void 0, void 0, function* () {
        function tryConnect() {
            return __awaiter(this, void 0, void 0, function* () {
                const tryConnectAfterTimeout = setTimeout.bind(undefined, tryConnect, 1000);
                const socket = yield factory();
                socket.on("connect", () => {
                    socket.removeListener("error", tryConnectAfterTimeout);
                    handler(socket);
                });
                socket.on("error", tryConnectAfterTimeout);
            });
        }
        yield tryConnect();
    });
}
exports.connectEventually = connectEventually;
function getHash(str, options) {
    return crypto
        .createHash((options && options.algorithm) || "sha256")
        .update(str)
        .digest((options && options.encoding) || "hex");
}
exports.getHash = getHash;
function connectEventuallyUntilTimeout(factory, timeout) {
    return __awaiter(this, void 0, void 0, function* () {
        return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
            let lastKnownError;
            let isResolved = false;
            const connectionTimer = setTimeout(function () {
                if (!isResolved) {
                    isResolved = true;
                    reject(lastKnownError || new Error(`Unable to connect for ${timeout}ms`));
                }
            }, timeout);
            function tryConnect() {
                return __awaiter(this, void 0, void 0, function* () {
                    const tryConnectAfterTimeout = (error) => {
                        if (isResolved) {
                            return;
                        }
                        lastKnownError = error;
                        setTimeout(tryConnect, 1000);
                    };
                    try {
                        const socket = yield factory();
                        socket.on("connect", () => {
                            socket.removeListener("error", tryConnectAfterTimeout);
                            isResolved = true;
                            clearTimeout(connectionTimer);
                            resolve(socket);
                        });
                        socket.on("error", tryConnectAfterTimeout);
                    }
                    catch (e) {
                        lastKnownError = e;
                        tryConnectAfterTimeout(e);
                    }
                });
            }
            yield tryConnect();
        }));
    });
}
exports.connectEventuallyUntilTimeout = connectEventuallyUntilTimeout;
function getProjectFilesConfig(opts) {
    const projectFilesConfig = {
        configuration: opts.isReleaseBuild
            ? constants_1.Configurations.Release.toLowerCase()
            : constants_1.Configurations.Debug.toLowerCase(),
    };
    return projectFilesConfig;
}
exports.getProjectFilesConfig = getProjectFilesConfig;
/**
 * Tries to find the process id (PID) of the specified application identifier.
 * This is specific implementation for iOS Simulator, where the running applications are real processes.
 * Their PIDs are printed in a specific format in the the logs, once the application is started.
 * @param {string} applicationIdentifier Application Identifier of the app for which we try to get the PID.
 * @param {string} logLine Line that may contain the PID of the process.
 * @returns {string} The PID of the searched application identifier in case it's found in the current line, null otherwise.
 */
function getPidFromiOSSimulatorLogs(applicationIdentifier, logLine) {
    if (logLine) {
        const pidRegExp = new RegExp(`${applicationIdentifier}:\\s?(\\d+)`);
        const pidMatch = logLine.match(pidRegExp);
        return pidMatch ? pidMatch[1] : null;
    }
    return null;
}
exports.getPidFromiOSSimulatorLogs = getPidFromiOSSimulatorLogs;
function getValueFromNestedObject(obj, key) {
    function _getValueRecursive(_obj, _key) {
        if (_.has(_obj, _key)) {
            return [_obj];
        }
        const res = [];
        _.forEach(_obj, (v, k) => {
            if (typeof v === "object" &&
                typeof k === "string" &&
                !_.startsWith(k, "$") &&
                !_.endsWith(k.toLowerCase(), "service") &&
                (v = _getValueRecursive(v, _key)).length) {
                res.push.apply(res, v);
            }
        });
        return res;
    }
    return _.head(_getValueRecursive(obj, key));
}
exports.getValueFromNestedObject = getValueFromNestedObject;
function getWinRegPropertyValue(key, propertyName) {
    return new Promise((resolve, reject) => {
        const Winreg = require("winreg");
        const regKey = new Winreg({
            hive: Winreg.HKLM,
            key: key,
        });
        regKey.get(propertyName, (err, value) => {
            if (err) {
                reject(err);
            }
            else {
                resolve(value);
            }
        });
    });
}
exports.getWinRegPropertyValue = getWinRegPropertyValue;
function stringify(value, replacer, space) {
    return JSON.stringify(value, replacer, space || 2);
}
exports.stringify = stringify;
//2019-01-07 18:29:50.745
function getFixedLengthDateString() {
    const currentDate = new Date();
    const year = currentDate.getFullYear();
    const month = getFormattedDateComponent(currentDate.getMonth() + 1);
    const day = getFormattedDateComponent(currentDate.getDate());
    const hour = getFormattedDateComponent(currentDate.getHours());
    const minutes = getFormattedDateComponent(currentDate.getMinutes());
    const seconds = getFormattedDateComponent(currentDate.getSeconds());
    const milliseconds = getFormattedMilliseconds(currentDate);
    return `${[year, month, day].join("-")} ${[hour, minutes, seconds].join(":")}.${milliseconds}`;
}
exports.getFixedLengthDateString = getFixedLengthDateString;
function getFormattedDateComponent(component) {
    const stringComponent = component.toString();
    return stringComponent.length === 1 ? `0${stringComponent}` : stringComponent;
}
exports.getFormattedDateComponent = getFormattedDateComponent;
function getFormattedMilliseconds(date) {
    let milliseconds = date.getMilliseconds().toString();
    while (milliseconds.length < 3) {
        milliseconds = `0${milliseconds}`;
    }
    return milliseconds;
}
exports.getFormattedMilliseconds = getFormattedMilliseconds;
//--- begin part copied from AngularJS
//The MIT License
//
//Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
//
//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.
const CLASS_NAME = /class\s+([A-Z].+?)(?:\s+.*?)?\{/;
const CONSTRUCTOR_ARGS = /constructor\s*([^\(]*)\(\s*([^\)]*)\)/m;
const FN_NAME_AND_ARGS = /^(?:function)?\s*([^\(]*)\(\s*([^\)]*)\)\s*(=>)?\s*[{_]/m;
const FN_ARG_SPLIT = /,/;
const FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
function annotate(fn) {
    let $inject, fnText, argDecl;
    if (typeof fn === "function") {
        if (!($inject = fn.$inject) || $inject.name !== fn.name) {
            $inject = { args: [], name: "" };
            fnText = fn.toString().replace(STRIP_COMMENTS, "");
            let nameMatch = fnText.match(CLASS_NAME);
            if (nameMatch) {
                argDecl = fnText.match(CONSTRUCTOR_ARGS);
            }
            else {
                nameMatch = argDecl = fnText.match(FN_NAME_AND_ARGS);
            }
            $inject.name = nameMatch && nameMatch[1];
            if (argDecl && fnText.length) {
                argDecl[2].split(FN_ARG_SPLIT).forEach((arg) => {
                    arg.replace(FN_ARG, (all, underscore, name) => $inject.args.push(name));
                });
            }
            fn.$inject = $inject;
        }
    }
    return $inject;
}
exports.annotate = annotate;
/**
 * Returns true if all Android signing options are provided, false otherwise.
 * @param {IAndroidSigningData} signingData The signing data to be validated.
 * @return {void}
 */
function hasValidAndroidSigning(signingData) {
    const isValid = signingData &&
        signingData.keyStorePath &&
        signingData.keyStorePassword &&
        signingData.keyStoreAlias &&
        signingData.keyStoreAliasPassword;
    return !!isValid;
}
exports.hasValidAndroidSigning = hasValidAndroidSigning;
//--- end part copied from AngularJS
//# sourceMappingURL=helpers.js.map