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