"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.string = exports.number = exports.userAgent = exports.driverVersion = exports.driverName = void 0;
exports.inherits = inherits;
exports.format = format;
exports.isFunction = isFunction;
exports.isObject = isObject;
exports.isDate = isDate;
exports.isArray = isArray;
exports.isString = isString;
exports.isBoolean = isBoolean;
exports.isNumber = isNumber;
exports.isPrivateKey = isPrivateKey;
exports.exists = exists;
exports.apply = apply;
exports.nextSleepTime = nextSleepTime;
exports.getJitteredSleepTime = getJitteredSleepTime;
exports.chooseRandom = chooseRandom;
exports.getNextSleepTime = getNextSleepTime;
exports.getJitter = getJitter;
exports.isLoginRequest = isLoginRequest;
exports.isRetryableHttpError = isRetryableHttpError;
exports.validateClientSessionKeepAliveHeartbeatFrequency = validateClientSessionKeepAliveHeartbeatFrequency;
exports.constructHostname = constructHostname;
exports.isPrivateLink = isPrivateLink;
exports.createOcspResponseCacheServerUrl = createOcspResponseCacheServerUrl;
exports.isPutCommand = isPutCommand;
exports.isGetCommand = isGetCommand;
exports.convertSmkIdToString = convertSmkIdToString;
exports.getCircularReplacer = getCircularReplacer;
exports.isCorrectSubdomain = isCorrectSubdomain;
exports.buildCredentialCacheKey = buildCredentialCacheKey;
exports.checkValidCustomCredentialManager = checkValidCustomCredentialManager;
exports.checkParametersDefined = checkParametersDefined;
exports.shouldRetryOktaAuth = shouldRetryOktaAuth;
exports.getDriverDirectory = getDriverDirectory;
exports.validatePath = validatePath;
exports.getEnvVar = getEnvVar;
exports.validateEmptyString = validateEmptyString;
exports.isNotEmptyAsString = isNotEmptyAsString;
exports.isNotEmptyString = isNotEmptyString;
exports.isEmptyObject = isEmptyObject;
exports.isWindows = isWindows;
exports.getFreePort = getFreePort;
exports.isPortOpen = isPortOpen;
exports.lstrip = lstrip;
exports.escapeHTML = escapeHTML;
exports.sleep = sleep;
exports.escapeRegex = escapeRegex;
const util_1 = __importDefault(require("util"));
const os_1 = __importDefault(require("os"));
const Logger = __importStar(require("./logger"));
const fs_1 = __importDefault(require("fs"));
const net_1 = __importDefault(require("net"));
const package_json_1 = require("../package.json");
Object.defineProperty(exports, "driverName", { enumerable: true, get: function () { return package_json_1.name; } });
Object.defineProperty(exports, "driverVersion", { enumerable: true, get: function () { return package_json_1.version; } });
const nodeJSVersion = process.version?.startsWith('v')
    ? process.version.substring(1)
    : process.version;
exports.userAgent = `JavaScript/${package_json_1.version} (${process.platform}-${process.arch}) NodeJS/${nodeJSVersion}`;
/**
 * Note: A simple wrapper around util.inherits() for now, but this might change
 * in the future.
 *
 * Inherits the prototype methods from one constructor into another. The
 * prototype of constructor will be set to a new object created from
 * superConstructor.
 *
 * @param constructor
 * @param superConstructor
 *
 * @returns {Object}
 */
function inherits(constructor, superConstructor) {
    return util_1.default.inherits.apply(util_1.default, [constructor, superConstructor]);
}
/**
 * Note: A simple wrapper around util.format() for now, but this will likely
 * change in the future.
 *
 * Returns a formatted string using the first argument as a printf-like format.
 *
 * The first argument is a string that contains zero or more placeholders.
 * Each placeholder is replaced with the converted value from its corresponding
 * argument. Supported placeholders are:
 *   %s - String.
 *   %d - Number (both integer and float).
 *   %j - JSON. Replaced with the string '[Circular]' if the argument contains
 *        circular references.
 *   %% - single percent sign ('%'). This does not consume an argument.
 *
 * If the placeholder does not have a corresponding argument, the placeholder is
 * not replaced.
 *
 * If there are more arguments than placeholders, the extra arguments are
 * coerced to strings (for objects and symbols, util.inspect() is used) and then
 * concatenated, delimited by a space.
 *
 * If the first argument is not a format string then util.format() returns a
 * string that is the concatenation of all its arguments separated by spaces.
 * Each argument is converted to a string with util.inspect().
 */
function format(format, ...params) {
    return util_1.default.format.apply(util_1.default, [format, ...params]);
}
/**
 * Determines if a given value is a function.
 */
function isFunction(value) {
    return !!value && typeof value === 'function';
}
const toString = Object.prototype.toString;
/**
 * Determines if a given value is an object.
 */
function isObject(value) {
    return toString.call(value) === '[object Object]';
}
/**
 * Determines if a given value is a Date.
 */
function isDate(value) {
    return toString.call(value) === '[object Date]';
}
/**
 * Determines if a given value is an array.
 */
function isArray(value) {
    return toString.call(value) === '[object Array]';
}
/**
 * Determines if a given value is a string.
 */
function isString(value) {
    return typeof value === 'string';
}
/**
 * Determines if a given value is a boolean.
 */
function isBoolean(value) {
    return typeof value === 'boolean';
}
/**
 * Determines if a given value is a number.
 */
function isNumber(value) {
    return typeof value === 'number' && isFinite(value);
}
/**
 * Determines if a given value is a private key string in pem format of type pkcs8.
 */
function isPrivateKey(value) {
    const trimmedValue = value.trim();
    // The private key is expected to be decrypted when set in the connection string
    // secret scanner complains about first check since it looks like private key, but it's only check
    // pragma: allowlist nextline secret
    return (trimmedValue.startsWith('-----BEGIN PRIVATE KEY-----') &&
        trimmedValue.endsWith('\n-----END PRIVATE KEY-----'));
}
/**
 * A collection of number-related utility functions.
 */
exports.number = {
    /**
     * Determines if a given value is a positive number.
     */
    isPositive: function (value) {
        return isNumber(value) && value > 0;
    },
    /**
     * Determines if a given value is a non-negative number.
     */
    isNonNegative: function (value) {
        return isNumber(value) && value >= 0;
    },
    /**
     * Determines if a given value is an integer.
     */
    isInteger: function (value) {
        return isNumber(value) && Math.floor(value) === value;
    },
    /**
     * Determines if a given value is a positive integer.
     */
    isPositiveInteger: function (value) {
        return this.isInteger(value) && value > 0;
    },
    /**
     * Determines if a given value is a non-negative integer.
     */
    isNonNegativeInteger: function (value) {
        return this.isInteger(value) && value >= 0;
    },
};
/**
 * A collection of string-related utility functions.
 */
exports.string = {
    /**
     * Determines if a given string is not null or empty.
     */
    isNotNullOrEmpty: function (value) {
        return isString(value) && value;
    },
};
/**
 * Determines if a given value is not null or undefined.
 *
 * @deprecated Just use if (!value) instead
 */
function exists(value) {
    return value !== null && value !== undefined;
}
/**
 * Shallow-copies everything from a source object into a destination object.
 *
 * @param {Object} dst the object to copy properties to.
 * @param {Object} src the object to copy properties from.
 */
function apply(dst, src) {
    // if both dst and src are objects, copy everything from src to dst
    if (isObject(dst) && isObject(src)) {
        for (const key in src) {
            if (Object.prototype.hasOwnProperty.call(src, key)) {
                dst[key] = src[key];
            }
        }
    }
    return dst;
}
/**
 * Returns the next sleep time calculated by exponential backoff with
 * decorrelated jitter.
 * sleep = min(cap, random_between(base, sleep * 3))
 * for more details, check out:
 * http://www.awsarchitectureblog.com/2015/03/backoff.html
 * @param base minimum seconds
 * @param cap maximum seconds
 * @param previousSleep previous sleep time
 */
function nextSleepTime(base, cap, previousSleep) {
    return Math.min(cap, Math.abs(previousSleep * 3 - base) * Math.random() + Math.min(base, previousSleep * 3));
}
/**
 * Return next sleep time calculated by the jitter rule.
 */
function getJitteredSleepTime(numofRetries, currentSleepTime, totalElapsedTime, maxRetryTimeout) {
    const nextsleep = getNextSleepTime(numofRetries, currentSleepTime);
    const sleep = maxRetryTimeout !== 0 ? Math.min(maxRetryTimeout - totalElapsedTime, nextsleep) : nextsleep;
    totalElapsedTime += sleep;
    return { sleep, totalElapsedTime };
}
/**
 * Choose one of the number between two numbers.
 */
function chooseRandom(firstNumber, secondNumber) {
    return Math.random() * (firstNumber - secondNumber) + secondNumber;
}
/**
 * return the next sleep Time.
 */
function getNextSleepTime(numofRetries, currentSleepTime) {
    const nextSleep = 2 ** numofRetries;
    return chooseRandom(currentSleepTime + getJitter(currentSleepTime), nextSleep + getJitter(currentSleepTime));
}
/**
 * return the jitter value.
 */
function getJitter(currentSleepTime) {
    const multiplicationFactor = chooseRandom(1, -1);
    return 0.5 * currentSleepTime * multiplicationFactor;
}
/**
 * Check whether the request is the login-request or not.
 */
function isLoginRequest(loginUrl) {
    const endPoints = ['/v1/login-request', '/authenticator-request'];
    return endPoints.some((endPoint) => loginUrl.includes(endPoint));
}
/**
 * Checks if the HTTP response code is retryable
 *
 * @param response HTTP response object
 * @param retry403 will retry HTTP 403?
 */
function isRetryableHttpError(response, retry403) {
    return (response &&
        ((response.statusCode >= 500 && response.statusCode < 600) ||
            (retry403 && response.statusCode === 403) ||
            response.statusCode === 408 ||
            response.statusCode === 429));
}
function validateClientSessionKeepAliveHeartbeatFrequency(input, masterValidity) {
    let heartbeatFrequency = input;
    const realMax = Math.floor(masterValidity / 4);
    const realMin = Math.floor(realMax / 4);
    if (input > realMax) {
        heartbeatFrequency = realMax;
    }
    else if (input < realMin) {
        heartbeatFrequency = realMin;
    }
    heartbeatFrequency = Math.floor(heartbeatFrequency);
    return heartbeatFrequency;
}
/**
 * Constructs host name using region and account
 *
 * @param region where the account is located
 * @param account which account to connect to
 */
function constructHostname(region, account) {
    let host;
    if (region === 'us-west-2') {
        host = account + '.snowflakecomputing.com';
    }
    else if (region != null) {
        if (account.indexOf('.') > 0) {
            account = account.substring(0, account.indexOf('.'));
        }
        if (region.startsWith('cn-') || region.startsWith('CN-')) {
            host = account + '.' + region + '.snowflakecomputing.cn';
        }
        else {
            host = account + '.' + region + '.snowflakecomputing.com';
        }
    }
    else {
        host = account + '.snowflakecomputing.com';
    }
    return host;
}
/**
 * Returns true if host indicates private link
 */
function isPrivateLink(host) {
    return host.toLowerCase().includes('privatelink.snowflakecomputing.');
}
function createOcspResponseCacheServerUrl(host) {
    return `http://ocsp.${host}/ocsp_response_cache.json`;
}
/**
 * Returns if command is a PUT command
 */
function isPutCommand(sqlText) {
    return sqlText.trim().substring(0, 3).toUpperCase() === 'PUT';
}
/**
 * Returns if command is a GET command
 */
function isGetCommand(sqlText) {
    return sqlText.trim().substring(0, 3).toUpperCase() === 'GET';
}
/**
 * Add double quotes to smkId's value to parse it as a string instead of integer
 * to preserve precision of numbers exceeding JavaScript's max safe integer
 * e.g (inputting 32621973126123526	outputs 32621973126123530)
 *
 * @param body the data in JSON
 */
function convertSmkIdToString(body) {
    return body.replace(/"smkId"(\s*):(\s*)([0-9]+)/g, '"smkId"$1:$2"$3"');
}
/**
 * Under some circumstances the object passed to JSON.stringify in exception handling
 * can contain circular reference, on which JSON.stringify bails out
 * MDN way of handling such error
 */
function getCircularReplacer() {
    const ancestors = [];
    return function (key, value) {
        if (typeof value !== 'object' || value === null) {
            return value;
        }
        // `this` is the object that value is contained in,
        // i.e., its direct parent.
        // @ts-ignore TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
        while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
            ancestors.pop();
        }
        if (ancestors.includes(value)) {
            return '[Circular]';
        }
        ancestors.push(value);
        return value;
    };
}
/**
 * Returns if the provided string is a valid subdomain.
 */
function isCorrectSubdomain(value) {
    const subdomainRegex = RegExp(/^\w+([.-]\w+)*$/i);
    return subdomainRegex.test(value);
}
function buildCredentialCacheKey(host, username, credType) {
    if (!host || !username || !credType) {
        Logger.getInstance().debug('Cannot build the credential cache key because one of host, username, and credType is null');
        return null;
    }
    return `{${host.toUpperCase()}}:{${username.toUpperCase()}}:{${credType.toUpperCase()}}`;
}
function checkValidCustomCredentialManager(customCredentialManager) {
    if (typeof customCredentialManager !== 'object') {
        return false;
    }
    const requireMethods = ['write', 'read', 'remove'];
    for (const method of requireMethods) {
        if (!Object.hasOwnProperty.call(customCredentialManager, method) ||
            typeof customCredentialManager[method] !== 'function') {
            return false;
        }
    }
    return true;
}
function checkParametersDefined(...parameters) {
    return parameters.every((element) => element !== undefined && element !== null);
}
function shouldRetryOktaAuth({ maxRetryTimeout, maxRetryCount, numRetries, startTime, remainingTimeout, }) {
    return ((maxRetryTimeout === 0 || Date.now() < startTime + remainingTimeout) &&
        numRetries <= maxRetryCount);
}
function getDriverDirectory() {
    return __dirname;
}
function validatePath(dir) {
    try {
        const stat = fs_1.default.statSync(dir);
        return stat.isDirectory();
    }
    catch {
        Logger.getInstance().error('The location is invalid. Please check this location is accessible or existing');
        return false;
    }
}
function getEnvVar(variable) {
    return process.env[variable.toLowerCase()] || process.env[variable.toUpperCase()];
}
function validateEmptyString(value) {
    return value !== '' ? value : undefined;
}
function isNotEmptyAsString(variable) {
    if (typeof variable === 'string') {
        return variable;
    }
    return exists(variable);
}
function isNotEmptyString(variable) {
    return exists(variable) && variable !== '';
}
/**
 * Checks Whether the object is empty (can be null or undefined) or not.
 */
function isEmptyObject(object) {
    if (!exists(object)) {
        return true;
    }
    if (typeof object !== 'object') {
        return false;
    }
    return Object.keys(object).length === 0;
}
function isWindows() {
    return os_1.default.platform() === 'win32';
}
async function getFreePort() {
    return new Promise((res) => {
        const srv = net_1.default.createServer();
        srv.listen(0, () => {
            // @ts-ignore TS2339: Property 'port' does not exist on type 'string | AddressInfo'
            const port = srv.address().port;
            srv.close(() => res(port));
        });
    });
}
async function isPortOpen(port) {
    return new Promise((resolve, reject) => {
        const s = net_1.default.createServer();
        s.once('error', (err) => {
            s.close();
            if (err['code'] === 'EADDRINUSE') {
                Logger.getInstance().trace(`Port: ${port} is not available. Verification failed`);
                reject('Port not available.');
            }
            else {
                Logger.getInstance().trace(`There is unexpected error during verification of port availability. Port: ${port}. Error: ${JSON.stringify(err)}`);
            }
        });
        s.once('listening', () => {
            s.close();
            Logger.getInstance().trace(`Closing server run for verification whether the port is available. Port: ${port}`);
            resolve('Listening');
        });
        s.listen(port);
    });
}
/**
 * Left strip the specified character from a string.
 */
function lstrip(str, remove) {
    while (str.length > 0 && remove.indexOf(str.charAt(0)) !== -1) {
        str = str.substr(1);
    }
    return str;
}
/**
 * This method transforms HTML special characters into their corresponding entity representations.
 */
function escapeHTML(value) {
    if (!exists(value)) {
        return value;
    }
    return value
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
}
function sleep(sleepTimeMs) {
    return new Promise((resolve) => setTimeout(resolve, sleepTimeMs));
}
function escapeRegex(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
//# sourceMappingURL=util.js.map