1*a5fb1ba8SEvan Bacon"use strict";
226ad19fcSEvan Bacon/**
326ad19fcSEvan Bacon * Copyright (c) 650 Industries.
426ad19fcSEvan Bacon * Copyright (c) Meta Platforms, Inc. and affiliates.
526ad19fcSEvan Bacon *
626ad19fcSEvan Bacon * This source code is licensed under the MIT license found in the
726ad19fcSEvan Bacon * LICENSE file in the root directory of this source tree.
826ad19fcSEvan Bacon */
9*a5fb1ba8SEvan BaconObject.defineProperty(exports, "__esModule", { value: true });
10*a5fb1ba8SEvan Baconexports.createStringifySafeWithLimits = void 0;
1126ad19fcSEvan Bacon/**
1226ad19fcSEvan Bacon * Tries to stringify with JSON.stringify and toString, but catches exceptions
1326ad19fcSEvan Bacon * (e.g. from circular objects) and always returns a string and never throws.
1426ad19fcSEvan Bacon */
15*a5fb1ba8SEvan Baconfunction createStringifySafeWithLimits(limits) {
1626ad19fcSEvan Bacon    const { maxDepth = Number.POSITIVE_INFINITY, maxStringLimit = Number.POSITIVE_INFINITY, maxArrayLimit = Number.POSITIVE_INFINITY, maxObjectKeysLimit = Number.POSITIVE_INFINITY, } = limits;
1726ad19fcSEvan Bacon    const stack = [];
1826ad19fcSEvan Bacon    function replacer(_key, value) {
1926ad19fcSEvan Bacon        while (stack.length && this !== stack[0]) {
2026ad19fcSEvan Bacon            stack.shift();
2126ad19fcSEvan Bacon        }
2226ad19fcSEvan Bacon        if (typeof value === 'string') {
2326ad19fcSEvan Bacon            const truncatedString = '...(truncated)...';
2426ad19fcSEvan Bacon            if (value.length > maxStringLimit + truncatedString.length) {
2526ad19fcSEvan Bacon                return value.substring(0, maxStringLimit) + truncatedString;
2626ad19fcSEvan Bacon            }
2726ad19fcSEvan Bacon            return value;
2826ad19fcSEvan Bacon        }
2926ad19fcSEvan Bacon        if (typeof value !== 'object' || value === null) {
3026ad19fcSEvan Bacon            return value;
3126ad19fcSEvan Bacon        }
3226ad19fcSEvan Bacon        let retval = value;
3326ad19fcSEvan Bacon        if (Array.isArray(value)) {
3426ad19fcSEvan Bacon            if (stack.length >= maxDepth) {
3526ad19fcSEvan Bacon                retval = `[ ... array with ${value.length} values ... ]`;
3626ad19fcSEvan Bacon            }
3726ad19fcSEvan Bacon            else if (value.length > maxArrayLimit) {
3826ad19fcSEvan Bacon                retval = value
3926ad19fcSEvan Bacon                    .slice(0, maxArrayLimit)
4026ad19fcSEvan Bacon                    .concat([`... extra ${value.length - maxArrayLimit} values truncated ...`]);
4126ad19fcSEvan Bacon            }
4226ad19fcSEvan Bacon        }
4326ad19fcSEvan Bacon        else {
4426ad19fcSEvan Bacon            // Add refinement after Array.isArray call.
4526ad19fcSEvan Bacon            if (typeof value !== 'object') {
4626ad19fcSEvan Bacon                throw new Error('This was already found earlier');
4726ad19fcSEvan Bacon            }
4826ad19fcSEvan Bacon            const keys = Object.keys(value);
4926ad19fcSEvan Bacon            if (stack.length >= maxDepth) {
5026ad19fcSEvan Bacon                retval = `{ ... object with ${keys.length} keys ... }`;
5126ad19fcSEvan Bacon            }
5226ad19fcSEvan Bacon            else if (keys.length > maxObjectKeysLimit) {
5326ad19fcSEvan Bacon                // Return a sample of the keys.
5426ad19fcSEvan Bacon                retval = {};
5526ad19fcSEvan Bacon                for (const k of keys.slice(0, maxObjectKeysLimit)) {
5626ad19fcSEvan Bacon                    retval[k] = value[k];
5726ad19fcSEvan Bacon                }
5826ad19fcSEvan Bacon                const truncatedKey = '...(truncated keys)...';
5926ad19fcSEvan Bacon                retval[truncatedKey] = keys.length - maxObjectKeysLimit;
6026ad19fcSEvan Bacon            }
6126ad19fcSEvan Bacon        }
6226ad19fcSEvan Bacon        stack.unshift(retval);
6326ad19fcSEvan Bacon        return retval;
6426ad19fcSEvan Bacon    }
6526ad19fcSEvan Bacon    return function stringifySafe(arg) {
6626ad19fcSEvan Bacon        if (arg === undefined) {
6726ad19fcSEvan Bacon            return 'undefined';
6826ad19fcSEvan Bacon        }
6926ad19fcSEvan Bacon        else if (arg === null) {
7026ad19fcSEvan Bacon            return 'null';
7126ad19fcSEvan Bacon        }
7226ad19fcSEvan Bacon        else if (typeof arg === 'function') {
7326ad19fcSEvan Bacon            try {
7426ad19fcSEvan Bacon                return arg.toString();
7526ad19fcSEvan Bacon            }
7626ad19fcSEvan Bacon            catch {
7726ad19fcSEvan Bacon                return '[function unknown]';
7826ad19fcSEvan Bacon            }
7926ad19fcSEvan Bacon        }
8026ad19fcSEvan Bacon        else if (arg instanceof Error) {
8126ad19fcSEvan Bacon            return arg.name + ': ' + arg.message;
8226ad19fcSEvan Bacon        }
8326ad19fcSEvan Bacon        else {
8426ad19fcSEvan Bacon            // Perform a try catch, just in case the object has a circular
8526ad19fcSEvan Bacon            // reference or stringify throws for some other reason.
8626ad19fcSEvan Bacon            try {
8726ad19fcSEvan Bacon                const ret = JSON.stringify(arg, replacer);
8826ad19fcSEvan Bacon                if (ret === undefined) {
8926ad19fcSEvan Bacon                    return '["' + typeof arg + '" failed to stringify]';
9026ad19fcSEvan Bacon                }
9126ad19fcSEvan Bacon                return ret;
9226ad19fcSEvan Bacon            }
9326ad19fcSEvan Bacon            catch {
9426ad19fcSEvan Bacon                if (typeof arg.toString === 'function') {
9526ad19fcSEvan Bacon                    try {
9626ad19fcSEvan Bacon                        // $FlowFixMe[incompatible-use] : toString shouldn't take any arguments in general.
9726ad19fcSEvan Bacon                        return arg.toString();
9826ad19fcSEvan Bacon                    }
9926ad19fcSEvan Bacon                    catch { }
10026ad19fcSEvan Bacon                }
10126ad19fcSEvan Bacon            }
10226ad19fcSEvan Bacon        }
10326ad19fcSEvan Bacon        return '["' + typeof arg + '" failed to stringify]';
10426ad19fcSEvan Bacon    };
10526ad19fcSEvan Bacon}
106*a5fb1ba8SEvan Baconexports.createStringifySafeWithLimits = createStringifySafeWithLimits;
10726ad19fcSEvan Baconconst stringifySafe = createStringifySafeWithLimits({
10826ad19fcSEvan Bacon    maxDepth: 10,
10926ad19fcSEvan Bacon    maxStringLimit: 100,
11026ad19fcSEvan Bacon    maxArrayLimit: 50,
11126ad19fcSEvan Bacon    maxObjectKeysLimit: 50,
11226ad19fcSEvan Bacon});
113*a5fb1ba8SEvan Baconexports.default = stringifySafe;
11426ad19fcSEvan Bacon//# sourceMappingURL=index.js.map