"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var is_1 = require("./is");
/**
 * Transforms Error object into an object literal with all it's attributes
 * attached to it.
 *
 * Based on: https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106
 *
 * @param error An Error containing all relevant information
 * @returns An object with all error properties
 */
function objectifyError(error) {
    // These properties are implemented as magical getters and don't show up in `for-in` loop
    var err = {
        message: error.message,
        name: error.name,
        stack: error.stack,
    };
    for (var i in error) {
        if (Object.prototype.hasOwnProperty.call(error, i)) {
            err[i] = error[i];
        }
    }
    return err;
}
var NAN_VALUE = '[NaN]';
var UNDEFINED_VALUE = '[undefined]';
/**
 * Serializer function used as 2nd argument to JSON.serialize in `serialize()` util function.
 */
function serializer() {
    var stack = [];
    var keys = [];
    var cycleReplacer = function (_, value) {
        if (stack[0] === value) {
            return '[Circular ~]';
        }
        return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join('.') + "]";
    };
    return function (key, value) {
        var currentValue = value;
        // NaN and undefined are not JSON.parseable, but we want to preserve this information
        if (is_1.isNaN(value)) {
            currentValue = NAN_VALUE;
        }
        else if (is_1.isUndefined(value)) {
            currentValue = UNDEFINED_VALUE;
        }
        if (stack.length > 0) {
            var thisPos = stack.indexOf(this);
            if (thisPos !== -1) {
                stack.splice(thisPos + 1);
                keys.splice(thisPos, Infinity, key);
            }
            else {
                stack.push(this);
                keys.push(key);
            }
            if (stack.indexOf(currentValue) !== -1) {
                currentValue = cycleReplacer.call(this, key, currentValue);
            }
        }
        else {
            stack.push(currentValue);
        }
        return currentValue instanceof Error ? objectifyError(currentValue) : currentValue;
    };
}
/**
 * Reviver function used as 2nd argument to JSON.parse in `deserialize()` util function.
 */
function reviver(_key, value) {
    // NaN and undefined are not JSON.parseable, but we want to preserve this information
    if (value === NAN_VALUE) {
        return NaN;
    }
    if (value === UNDEFINED_VALUE) {
        return undefined;
    }
    return value;
}
/**
 * Serializes the given object into a string.
 * Like JSON.stringify, but doesn't throw on circular references.
 * Based on a `json-stringify-safe` package and modified to handle Errors serialization.
 *
 * The object must be serializable, i.e.:
 *  - Only primitive types are allowed (object, array, number, string, boolean)
 *  - Its depth should be considerably low for performance reasons
 *
 * @param object A JSON-serializable object.
 * @returns A string containing the serialized object.
 */
function serialize(object) {
    return JSON.stringify(object, serializer());
}
exports.serialize = serialize;
/**
 * Deserializes an object from a string previously serialized with
 * {@link serialize}.
 *
 * @param str A serialized object.
 * @returns The deserialized object.
 */
function deserialize(str) {
    return JSON.parse(str, reviver);
}
exports.deserialize = deserialize;
/**
 * Creates a deep copy of the given object.
 *
 * The object must be serializable, i.e.:
 *  - It must not contain any cycles
 *  - Only primitive types are allowed (object, array, number, string, boolean)
 *  - Its depth should be considerably low for performance reasons
 *
 * @param object A JSON-serializable object.
 * @returns The object clone.
 */
function clone(object) {
    return deserialize(serialize(object));
}
exports.clone = clone;
/**
 * Wrap a given object method with a higher-order function
 *
 * @param source An object that contains a method to be wrapped.
 * @param name A name of method to be wrapped.
 * @param replacement A function that should be used to wrap a given method.
 * @returns void
 */
function fill(source, name, replacement) {
    if (!(name in source) || source[name].__sentry__) {
        return;
    }
    var original = source[name];
    var wrapped = replacement(original);
    wrapped.__sentry__ = true;
    wrapped.__sentry_original__ = original;
    wrapped.__sentry_wrapped__ = wrapped;
    source[name] = wrapped;
}
exports.fill = fill;
/**
 * Encodes given object into url-friendly format
 *
 * @param object An object that contains serializable values
 * @returns string Encoded
 */
function urlEncode(object) {
    return Object.keys(object)
        .map(
    // tslint:disable-next-line:no-unsafe-any
    function (key) { return encodeURIComponent(key) + "=" + encodeURIComponent(object[key]); })
        .join('&');
}
exports.urlEncode = urlEncode;
// Default Node.js REPL depth
var MAX_SERIALIZE_EXCEPTION_DEPTH = 3;
// 100kB, as 200kB is max payload size, so half sounds reasonable
var MAX_SERIALIZE_EXCEPTION_SIZE = 100 * 1024;
var MAX_SERIALIZE_KEYS_LENGTH = 40;
/** JSDoc */
function utf8Length(value) {
    // tslint:disable-next-line:no-bitwise
    return ~-encodeURI(value).split(/%..|./).length;
}
/** JSDoc */
function jsonSize(value) {
    return utf8Length(JSON.stringify(value));
}
/** JSDoc */
function serializeValue(value) {
    var maxLength = 40;
    if (typeof value === 'string') {
        return value.length <= maxLength ? value : value.substr(0, maxLength - 1) + "\u2026";
    }
    else if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'undefined') {
        return value;
    }
    else if (is_1.isNaN(value)) {
        // NaN and undefined are not JSON.parseable, but we want to preserve this information
        return '[NaN]';
    }
    else if (is_1.isUndefined(value)) {
        return '[undefined]';
    }
    var type = Object.prototype.toString.call(value);
    // Node.js REPL notation
    if (type === '[object Object]') {
        return '[Object]';
    }
    if (type === '[object Array]') {
        return '[Array]';
    }
    if (type === '[object Function]') {
        var name_1 = value.name;
        return name_1 ? "[Function: " + name_1 + "]" : '[Function]';
    }
    return value;
}
/** JSDoc */
function serializeObject(value, depth) {
    if (depth === 0) {
        return serializeValue(value);
    }
    if (is_1.isPlainObject(value)) {
        var serialized_1 = {};
        var val_1 = value;
        Object.keys(val_1).forEach(function (key) {
            serialized_1[key] = serializeObject(val_1[key], depth - 1);
        });
        return serialized_1;
    }
    else if (Array.isArray(value)) {
        var val = value;
        return val.map(function (v) { return serializeObject(v, depth - 1); });
    }
    return serializeValue(value);
}
exports.serializeObject = serializeObject;
/** JSDoc */
function limitObjectDepthToSize(object, depth, maxSize) {
    if (depth === void 0) { depth = MAX_SERIALIZE_EXCEPTION_DEPTH; }
    if (maxSize === void 0) { maxSize = MAX_SERIALIZE_EXCEPTION_SIZE; }
    var serialized = serializeObject(object, depth);
    if (jsonSize(serialize(serialized)) > maxSize) {
        return limitObjectDepthToSize(object, depth - 1);
    }
    return serialized;
}
exports.limitObjectDepthToSize = limitObjectDepthToSize;
/** JSDoc */
function serializeKeysToEventMessage(keys, maxLength) {
    if (maxLength === void 0) { maxLength = MAX_SERIALIZE_KEYS_LENGTH; }
    if (!keys.length) {
        return '[object has no keys]';
    }
    if (keys[0].length >= maxLength) {
        return keys[0];
    }
    for (var includedKeys = keys.length; includedKeys > 0; includedKeys--) {
        var serialized = keys.slice(0, includedKeys).join(', ');
        if (serialized.length > maxLength) {
            continue;
        }
        if (includedKeys === keys.length) {
            return serialized;
        }
        return serialized + "\u2026";
    }
    return '';
}
exports.serializeKeysToEventMessage = serializeKeysToEventMessage;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill
/** JSDoc */
function assign(target) {
    var args = [];
    for (var _i = 1; _i < arguments.length; _i++) {
        args[_i - 1] = arguments[_i];
    }
    if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
    }
    var to = Object(target);
    // tslint:disable-next-line
    for (var i = 0; i < args.length; i++) {
        var source = args[i];
        if (source !== null) {
            for (var nextKey in source) {
                if (Object.prototype.hasOwnProperty.call(source, nextKey)) {
                    to[nextKey] = source[nextKey];
                }
            }
        }
    }
    return to;
}
exports.assign = assign;
//# sourceMappingURL=object.js.map