1/**
2 * Copyright (c) 650 Industries.
3 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8/**
9 * Tries to stringify with JSON.stringify and toString, but catches exceptions
10 * (e.g. from circular objects) and always returns a string and never throws.
11 */
12export function createStringifySafeWithLimits(limits) {
13    const { maxDepth = Number.POSITIVE_INFINITY, maxStringLimit = Number.POSITIVE_INFINITY, maxArrayLimit = Number.POSITIVE_INFINITY, maxObjectKeysLimit = Number.POSITIVE_INFINITY, } = limits;
14    const stack = [];
15    function replacer(_key, value) {
16        while (stack.length && this !== stack[0]) {
17            stack.shift();
18        }
19        if (typeof value === 'string') {
20            const truncatedString = '...(truncated)...';
21            if (value.length > maxStringLimit + truncatedString.length) {
22                return value.substring(0, maxStringLimit) + truncatedString;
23            }
24            return value;
25        }
26        if (typeof value !== 'object' || value === null) {
27            return value;
28        }
29        let retval = value;
30        if (Array.isArray(value)) {
31            if (stack.length >= maxDepth) {
32                retval = `[ ... array with ${value.length} values ... ]`;
33            }
34            else if (value.length > maxArrayLimit) {
35                retval = value
36                    .slice(0, maxArrayLimit)
37                    .concat([`... extra ${value.length - maxArrayLimit} values truncated ...`]);
38            }
39        }
40        else {
41            // Add refinement after Array.isArray call.
42            if (typeof value !== 'object') {
43                throw new Error('This was already found earlier');
44            }
45            const keys = Object.keys(value);
46            if (stack.length >= maxDepth) {
47                retval = `{ ... object with ${keys.length} keys ... }`;
48            }
49            else if (keys.length > maxObjectKeysLimit) {
50                // Return a sample of the keys.
51                retval = {};
52                for (const k of keys.slice(0, maxObjectKeysLimit)) {
53                    retval[k] = value[k];
54                }
55                const truncatedKey = '...(truncated keys)...';
56                retval[truncatedKey] = keys.length - maxObjectKeysLimit;
57            }
58        }
59        stack.unshift(retval);
60        return retval;
61    }
62    return function stringifySafe(arg) {
63        if (arg === undefined) {
64            return 'undefined';
65        }
66        else if (arg === null) {
67            return 'null';
68        }
69        else if (typeof arg === 'function') {
70            try {
71                return arg.toString();
72            }
73            catch {
74                return '[function unknown]';
75            }
76        }
77        else if (arg instanceof Error) {
78            return arg.name + ': ' + arg.message;
79        }
80        else {
81            // Perform a try catch, just in case the object has a circular
82            // reference or stringify throws for some other reason.
83            try {
84                const ret = JSON.stringify(arg, replacer);
85                if (ret === undefined) {
86                    return '["' + typeof arg + '" failed to stringify]';
87                }
88                return ret;
89            }
90            catch {
91                if (typeof arg.toString === 'function') {
92                    try {
93                        // $FlowFixMe[incompatible-use] : toString shouldn't take any arguments in general.
94                        return arg.toString();
95                    }
96                    catch { }
97                }
98            }
99        }
100        return '["' + typeof arg + '" failed to stringify]';
101    };
102}
103const stringifySafe = createStringifySafeWithLimits({
104    maxDepth: 10,
105    maxStringLimit: 100,
106    maxArrayLimit: 50,
107    maxObjectKeysLimit: 50,
108});
109export default stringifySafe;
110//# sourceMappingURL=index.js.map