/**
* Caps an arbitrary JS value down to something safe to log or hand back to
* a caller — long strings get an ellipsis, long arrays keep only their
* first items (with a note of how many were dropped), and anything that
* isn't plain JSON-shaped (BigInt, an Error, a circular reference) is
* coerced to a readable string instead of throwing.
*
* Unlike a flat string truncation (e.g. this file's own `truncateDebug` in
* `ScenidCloudService.class.js`), the result stays a real object/array —
* safe for a caller to read `.length` or a field off of, not just print.
*
* Used by `InsightsService`'s dry-run write mocks (see
* `InsightsService.class.js`'s `_guardWrite`) so a script that tries to
* mock-insert a huge batch doesn't get the whole batch echoed back.
*
* @module truncate
*/
const MAX_STRING_LENGTH = 2000
const MAX_ARRAY_ITEMS = 20
const truncateString = (value, maxStringLength) => (
value.length > maxStringLength ? `${value.slice(0, maxStringLength)}…` : value
)
const truncateValue = (value, { maxStringLength = MAX_STRING_LENGTH, maxArrayItems = MAX_ARRAY_ITEMS, seen = new WeakSet() } = {}) => {
if (value === undefined || value === null) return value
if (typeof value === 'bigint') return value.toString()
if (typeof value === 'string') return truncateString(value, maxStringLength)
if (typeof value !== 'object') return value
if (value instanceof Error) return `${value.name}: ${truncateString(value.message, maxStringLength)}`
if (seen.has(value)) return '[circular]'
seen.add(value)
if (Array.isArray(value)) {
const items = value.slice(0, maxArrayItems).map(item => truncateValue(item, { maxStringLength, maxArrayItems, seen }))
if (value.length > maxArrayItems) items.push(`…${value.length - maxArrayItems} more`)
return items
}
const result = {}
Object.entries(value).forEach(([key, entryValue]) => {
result[key] = truncateValue(entryValue, { maxStringLength, maxArrayItems, seen })
})
return result
}
export default truncateValue
Source