You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							671 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
	
	
							671 lines
						
					
					
						
							21 KiB
						
					
					
				| 'use strict'
 | |
| 
 | |
| const { hasOwnProperty } = Object.prototype
 | |
| 
 | |
| const stringify = configure()
 | |
| 
 | |
| // @ts-expect-error
 | |
| stringify.configure = configure
 | |
| // @ts-expect-error
 | |
| stringify.stringify = stringify
 | |
| 
 | |
| // @ts-expect-error
 | |
| stringify.default = stringify
 | |
| 
 | |
| // @ts-expect-error used for named export
 | |
| exports.stringify = stringify
 | |
| // @ts-expect-error used for named export
 | |
| exports.configure = configure
 | |
| 
 | |
| module.exports = stringify
 | |
| 
 | |
| // eslint-disable-next-line no-control-regex
 | |
| const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/
 | |
| const strEscapeSequencesReplacer = new RegExp(strEscapeSequencesRegExp, 'g')
 | |
| 
 | |
| // Escaped special characters. Use empty strings to fill up unused entries.
 | |
| const meta = [
 | |
|   '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
 | |
|   '\\u0005', '\\u0006', '\\u0007', '\\b', '\\t',
 | |
|   '\\n', '\\u000b', '\\f', '\\r', '\\u000e',
 | |
|   '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013',
 | |
|   '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018',
 | |
|   '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d',
 | |
|   '\\u001e', '\\u001f', '', '', '\\"',
 | |
|   '', '', '', '', '', '', '', '', '', '',
 | |
|   '', '', '', '', '', '', '', '', '', '',
 | |
|   '', '', '', '', '', '', '', '', '', '',
 | |
|   '', '', '', '', '', '', '', '', '', '',
 | |
|   '', '', '', '', '', '', '', '', '', '',
 | |
|   '', '', '', '', '', '', '', '\\\\'
 | |
| ]
 | |
| 
 | |
| function escapeFn (str) {
 | |
|   if (str.length === 2) {
 | |
|     const charCode = str.charCodeAt(1)
 | |
|     return `${str[0]}\\u${charCode.toString(16)}`
 | |
|   }
 | |
|   const charCode = str.charCodeAt(0)
 | |
|   return meta.length > charCode
 | |
|     ? meta[charCode]
 | |
|     : `\\u${charCode.toString(16)}`
 | |
| }
 | |
| 
 | |
| // Escape C0 control characters, double quotes, the backslash and every code
 | |
| // unit with a numeric value in the inclusive range 0xD800 to 0xDFFF.
 | |
| function strEscape (str) {
 | |
|   // Some magic numbers that worked out fine while benchmarking with v8 8.0
 | |
|   if (str.length < 5000 && !strEscapeSequencesRegExp.test(str)) {
 | |
|     return str
 | |
|   }
 | |
|   if (str.length > 100) {
 | |
|     return str.replace(strEscapeSequencesReplacer, escapeFn)
 | |
|   }
 | |
|   let result = ''
 | |
|   let last = 0
 | |
|   for (let i = 0; i < str.length; i++) {
 | |
|     const point = str.charCodeAt(i)
 | |
|     if (point === 34 || point === 92 || point < 32) {
 | |
|       result += `${str.slice(last, i)}${meta[point]}`
 | |
|       last = i + 1
 | |
|     } else if (point >= 0xd800 && point <= 0xdfff) {
 | |
|       if (point <= 0xdbff && i + 1 < str.length) {
 | |
|         const nextPoint = str.charCodeAt(i + 1)
 | |
|         if (nextPoint >= 0xdc00 && nextPoint <= 0xdfff) {
 | |
|           i++
 | |
|           continue
 | |
|         }
 | |
|       }
 | |
|       result += `${str.slice(last, i)}\\u${point.toString(16)}`
 | |
|       last = i + 1
 | |
|     }
 | |
|   }
 | |
|   result += str.slice(last)
 | |
|   return result
 | |
| }
 | |
| 
 | |
| function insertSort (array) {
 | |
|   // Insertion sort is very efficient for small input sizes but it has a bad
 | |
|   // worst case complexity. Thus, use native array sort for bigger values.
 | |
|   if (array.length > 2e2) {
 | |
|     return array.sort()
 | |
|   }
 | |
|   for (let i = 1; i < array.length; i++) {
 | |
|     const currentValue = array[i]
 | |
|     let position = i
 | |
|     while (position !== 0 && array[position - 1] > currentValue) {
 | |
|       array[position] = array[position - 1]
 | |
|       position--
 | |
|     }
 | |
|     array[position] = currentValue
 | |
|   }
 | |
|   return array
 | |
| }
 | |
| 
 | |
| const typedArrayPrototypeGetSymbolToStringTag =
 | |
|   Object.getOwnPropertyDescriptor(
 | |
|     Object.getPrototypeOf(
 | |
|       Object.getPrototypeOf(
 | |
|         new Int8Array()
 | |
|       )
 | |
|     ),
 | |
|     Symbol.toStringTag
 | |
|   ).get
 | |
| 
 | |
| function isTypedArrayWithEntries (value) {
 | |
|   return typedArrayPrototypeGetSymbolToStringTag.call(value) !== undefined && value.length !== 0
 | |
| }
 | |
| 
 | |
| function stringifyTypedArray (array, separator, maximumBreadth) {
 | |
|   if (array.length < maximumBreadth) {
 | |
|     maximumBreadth = array.length
 | |
|   }
 | |
|   const whitespace = separator === ',' ? '' : ' '
 | |
|   let res = `"0":${whitespace}${array[0]}`
 | |
|   for (let i = 1; i < maximumBreadth; i++) {
 | |
|     res += `${separator}"${i}":${whitespace}${array[i]}`
 | |
|   }
 | |
|   return res
 | |
| }
 | |
| 
 | |
| function getCircularValueOption (options) {
 | |
|   if (hasOwnProperty.call(options, 'circularValue')) {
 | |
|     const circularValue = options.circularValue
 | |
|     if (typeof circularValue === 'string') {
 | |
|       return `"${circularValue}"`
 | |
|     }
 | |
|     if (circularValue == null) {
 | |
|       return circularValue
 | |
|     }
 | |
|     if (circularValue === Error || circularValue === TypeError) {
 | |
|       return {
 | |
|         toString () {
 | |
|           throw new TypeError('Converting circular structure to JSON')
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     throw new TypeError('The "circularValue" argument must be of type string or the value null or undefined')
 | |
|   }
 | |
|   return '"[Circular]"'
 | |
| }
 | |
| 
 | |
| function getBooleanOption (options, key) {
 | |
|   let value
 | |
|   if (hasOwnProperty.call(options, key)) {
 | |
|     value = options[key]
 | |
|     if (typeof value !== 'boolean') {
 | |
|       throw new TypeError(`The "${key}" argument must be of type boolean`)
 | |
|     }
 | |
|   }
 | |
|   return value === undefined ? true : value
 | |
| }
 | |
| 
 | |
| function getPositiveIntegerOption (options, key) {
 | |
|   let value
 | |
|   if (hasOwnProperty.call(options, key)) {
 | |
|     value = options[key]
 | |
|     if (typeof value !== 'number') {
 | |
|       throw new TypeError(`The "${key}" argument must be of type number`)
 | |
|     }
 | |
|     if (!Number.isInteger(value)) {
 | |
|       throw new TypeError(`The "${key}" argument must be an integer`)
 | |
|     }
 | |
|     if (value < 1) {
 | |
|       throw new RangeError(`The "${key}" argument must be >= 1`)
 | |
|     }
 | |
|   }
 | |
|   return value === undefined ? Infinity : value
 | |
| }
 | |
| 
 | |
| function getItemCount (number) {
 | |
|   if (number === 1) {
 | |
|     return '1 item'
 | |
|   }
 | |
|   return `${number} items`
 | |
| }
 | |
| 
 | |
| function getUniqueReplacerSet (replacerArray) {
 | |
|   const replacerSet = new Set()
 | |
|   for (const value of replacerArray) {
 | |
|     if (typeof value === 'string' || typeof value === 'number') {
 | |
|       replacerSet.add(String(value))
 | |
|     }
 | |
|   }
 | |
|   return replacerSet
 | |
| }
 | |
| 
 | |
| function getStrictOption (options) {
 | |
|   if (hasOwnProperty.call(options, 'strict')) {
 | |
|     const value = options.strict
 | |
|     if (typeof value !== 'boolean') {
 | |
|       throw new TypeError('The "strict" argument must be of type boolean')
 | |
|     }
 | |
|     if (value) {
 | |
|       return (value) => {
 | |
|         let message = `Object can not safely be stringified. Received type ${typeof value}`
 | |
|         if (typeof value !== 'function') message += ` (${value.toString()})`
 | |
|         throw new Error(message)
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| function configure (options) {
 | |
|   options = { ...options }
 | |
|   const fail = getStrictOption(options)
 | |
|   if (fail) {
 | |
|     if (options.bigint === undefined) {
 | |
|       options.bigint = false
 | |
|     }
 | |
|     if (!('circularValue' in options)) {
 | |
|       options.circularValue = Error
 | |
|     }
 | |
|   }
 | |
|   const circularValue = getCircularValueOption(options)
 | |
|   const bigint = getBooleanOption(options, 'bigint')
 | |
|   const deterministic = getBooleanOption(options, 'deterministic')
 | |
|   const maximumDepth = getPositiveIntegerOption(options, 'maximumDepth')
 | |
|   const maximumBreadth = getPositiveIntegerOption(options, 'maximumBreadth')
 | |
| 
 | |
|   function stringifyFnReplacer (key, parent, stack, replacer, spacer, indentation) {
 | |
|     let value = parent[key]
 | |
| 
 | |
|     if (typeof value === 'object' && value !== null && typeof value.toJSON === 'function') {
 | |
|       value = value.toJSON(key)
 | |
|     }
 | |
|     value = replacer.call(parent, key, value)
 | |
| 
 | |
|     switch (typeof value) {
 | |
|       case 'string':
 | |
|         return `"${strEscape(value)}"`
 | |
|       case 'object': {
 | |
|         if (value === null) {
 | |
|           return 'null'
 | |
|         }
 | |
|         if (stack.indexOf(value) !== -1) {
 | |
|           return circularValue
 | |
|         }
 | |
| 
 | |
|         let res = ''
 | |
|         let join = ','
 | |
|         const originalIndentation = indentation
 | |
| 
 | |
|         if (Array.isArray(value)) {
 | |
|           if (value.length === 0) {
 | |
|             return '[]'
 | |
|           }
 | |
|           if (maximumDepth < stack.length + 1) {
 | |
|             return '"[Array]"'
 | |
|           }
 | |
|           stack.push(value)
 | |
|           if (spacer !== '') {
 | |
|             indentation += spacer
 | |
|             res += `\n${indentation}`
 | |
|             join = `,\n${indentation}`
 | |
|           }
 | |
|           const maximumValuesToStringify = Math.min(value.length, maximumBreadth)
 | |
|           let i = 0
 | |
|           for (; i < maximumValuesToStringify - 1; i++) {
 | |
|             const tmp = stringifyFnReplacer(i, value, stack, replacer, spacer, indentation)
 | |
|             res += tmp !== undefined ? tmp : 'null'
 | |
|             res += join
 | |
|           }
 | |
|           const tmp = stringifyFnReplacer(i, value, stack, replacer, spacer, indentation)
 | |
|           res += tmp !== undefined ? tmp : 'null'
 | |
|           if (value.length - 1 > maximumBreadth) {
 | |
|             const removedKeys = value.length - maximumBreadth - 1
 | |
|             res += `${join}"... ${getItemCount(removedKeys)} not stringified"`
 | |
|           }
 | |
|           if (spacer !== '') {
 | |
|             res += `\n${originalIndentation}`
 | |
|           }
 | |
|           stack.pop()
 | |
|           return `[${res}]`
 | |
|         }
 | |
| 
 | |
|         let keys = Object.keys(value)
 | |
|         const keyLength = keys.length
 | |
|         if (keyLength === 0) {
 | |
|           return '{}'
 | |
|         }
 | |
|         if (maximumDepth < stack.length + 1) {
 | |
|           return '"[Object]"'
 | |
|         }
 | |
|         let whitespace = ''
 | |
|         let separator = ''
 | |
|         if (spacer !== '') {
 | |
|           indentation += spacer
 | |
|           join = `,\n${indentation}`
 | |
|           whitespace = ' '
 | |
|         }
 | |
|         let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth)
 | |
|         if (isTypedArrayWithEntries(value)) {
 | |
|           res += stringifyTypedArray(value, join, maximumBreadth)
 | |
|           keys = keys.slice(value.length)
 | |
|           maximumPropertiesToStringify -= value.length
 | |
|           separator = join
 | |
|         }
 | |
|         if (deterministic) {
 | |
|           keys = insertSort(keys)
 | |
|         }
 | |
|         stack.push(value)
 | |
|         for (let i = 0; i < maximumPropertiesToStringify; i++) {
 | |
|           const key = keys[i]
 | |
|           const tmp = stringifyFnReplacer(key, value, stack, replacer, spacer, indentation)
 | |
|           if (tmp !== undefined) {
 | |
|             res += `${separator}"${strEscape(key)}":${whitespace}${tmp}`
 | |
|             separator = join
 | |
|           }
 | |
|         }
 | |
|         if (keyLength > maximumBreadth) {
 | |
|           const removedKeys = keyLength - maximumBreadth
 | |
|           res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`
 | |
|           separator = join
 | |
|         }
 | |
|         if (spacer !== '' && separator.length > 1) {
 | |
|           res = `\n${indentation}${res}\n${originalIndentation}`
 | |
|         }
 | |
|         stack.pop()
 | |
|         return `{${res}}`
 | |
|       }
 | |
|       case 'number':
 | |
|         return isFinite(value) ? String(value) : fail ? fail(value) : 'null'
 | |
|       case 'boolean':
 | |
|         return value === true ? 'true' : 'false'
 | |
|       case 'undefined':
 | |
|         return undefined
 | |
|       case 'bigint':
 | |
|         if (bigint) {
 | |
|           return String(value)
 | |
|         }
 | |
|         // fallthrough
 | |
|       default:
 | |
|         return fail ? fail(value) : undefined
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function stringifyArrayReplacer (key, value, stack, replacer, spacer, indentation) {
 | |
|     if (typeof value === 'object' && value !== null && typeof value.toJSON === 'function') {
 | |
|       value = value.toJSON(key)
 | |
|     }
 | |
| 
 | |
|     switch (typeof value) {
 | |
|       case 'string':
 | |
|         return `"${strEscape(value)}"`
 | |
|       case 'object': {
 | |
|         if (value === null) {
 | |
|           return 'null'
 | |
|         }
 | |
|         if (stack.indexOf(value) !== -1) {
 | |
|           return circularValue
 | |
|         }
 | |
| 
 | |
|         const originalIndentation = indentation
 | |
|         let res = ''
 | |
|         let join = ','
 | |
| 
 | |
|         if (Array.isArray(value)) {
 | |
|           if (value.length === 0) {
 | |
|             return '[]'
 | |
|           }
 | |
|           if (maximumDepth < stack.length + 1) {
 | |
|             return '"[Array]"'
 | |
|           }
 | |
|           stack.push(value)
 | |
|           if (spacer !== '') {
 | |
|             indentation += spacer
 | |
|             res += `\n${indentation}`
 | |
|             join = `,\n${indentation}`
 | |
|           }
 | |
|           const maximumValuesToStringify = Math.min(value.length, maximumBreadth)
 | |
|           let i = 0
 | |
|           for (; i < maximumValuesToStringify - 1; i++) {
 | |
|             const tmp = stringifyArrayReplacer(i, value[i], stack, replacer, spacer, indentation)
 | |
|             res += tmp !== undefined ? tmp : 'null'
 | |
|             res += join
 | |
|           }
 | |
|           const tmp = stringifyArrayReplacer(i, value[i], stack, replacer, spacer, indentation)
 | |
|           res += tmp !== undefined ? tmp : 'null'
 | |
|           if (value.length - 1 > maximumBreadth) {
 | |
|             const removedKeys = value.length - maximumBreadth - 1
 | |
|             res += `${join}"... ${getItemCount(removedKeys)} not stringified"`
 | |
|           }
 | |
|           if (spacer !== '') {
 | |
|             res += `\n${originalIndentation}`
 | |
|           }
 | |
|           stack.pop()
 | |
|           return `[${res}]`
 | |
|         }
 | |
|         stack.push(value)
 | |
|         let whitespace = ''
 | |
|         if (spacer !== '') {
 | |
|           indentation += spacer
 | |
|           join = `,\n${indentation}`
 | |
|           whitespace = ' '
 | |
|         }
 | |
|         let separator = ''
 | |
|         for (const key of replacer) {
 | |
|           const tmp = stringifyArrayReplacer(key, value[key], stack, replacer, spacer, indentation)
 | |
|           if (tmp !== undefined) {
 | |
|             res += `${separator}"${strEscape(key)}":${whitespace}${tmp}`
 | |
|             separator = join
 | |
|           }
 | |
|         }
 | |
|         if (spacer !== '' && separator.length > 1) {
 | |
|           res = `\n${indentation}${res}\n${originalIndentation}`
 | |
|         }
 | |
|         stack.pop()
 | |
|         return `{${res}}`
 | |
|       }
 | |
|       case 'number':
 | |
|         return isFinite(value) ? String(value) : fail ? fail(value) : 'null'
 | |
|       case 'boolean':
 | |
|         return value === true ? 'true' : 'false'
 | |
|       case 'undefined':
 | |
|         return undefined
 | |
|       case 'bigint':
 | |
|         if (bigint) {
 | |
|           return String(value)
 | |
|         }
 | |
|         // fallthrough
 | |
|       default:
 | |
|         return fail ? fail(value) : undefined
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function stringifyIndent (key, value, stack, spacer, indentation) {
 | |
|     switch (typeof value) {
 | |
|       case 'string':
 | |
|         return `"${strEscape(value)}"`
 | |
|       case 'object': {
 | |
|         if (value === null) {
 | |
|           return 'null'
 | |
|         }
 | |
|         if (typeof value.toJSON === 'function') {
 | |
|           value = value.toJSON(key)
 | |
|           // Prevent calling `toJSON` again.
 | |
|           if (typeof value !== 'object') {
 | |
|             return stringifyIndent(key, value, stack, spacer, indentation)
 | |
|           }
 | |
|           if (value === null) {
 | |
|             return 'null'
 | |
|           }
 | |
|         }
 | |
|         if (stack.indexOf(value) !== -1) {
 | |
|           return circularValue
 | |
|         }
 | |
|         const originalIndentation = indentation
 | |
| 
 | |
|         if (Array.isArray(value)) {
 | |
|           if (value.length === 0) {
 | |
|             return '[]'
 | |
|           }
 | |
|           if (maximumDepth < stack.length + 1) {
 | |
|             return '"[Array]"'
 | |
|           }
 | |
|           stack.push(value)
 | |
|           indentation += spacer
 | |
|           let res = `\n${indentation}`
 | |
|           const join = `,\n${indentation}`
 | |
|           const maximumValuesToStringify = Math.min(value.length, maximumBreadth)
 | |
|           let i = 0
 | |
|           for (; i < maximumValuesToStringify - 1; i++) {
 | |
|             const tmp = stringifyIndent(i, value[i], stack, spacer, indentation)
 | |
|             res += tmp !== undefined ? tmp : 'null'
 | |
|             res += join
 | |
|           }
 | |
|           const tmp = stringifyIndent(i, value[i], stack, spacer, indentation)
 | |
|           res += tmp !== undefined ? tmp : 'null'
 | |
|           if (value.length - 1 > maximumBreadth) {
 | |
|             const removedKeys = value.length - maximumBreadth - 1
 | |
|             res += `${join}"... ${getItemCount(removedKeys)} not stringified"`
 | |
|           }
 | |
|           res += `\n${originalIndentation}`
 | |
|           stack.pop()
 | |
|           return `[${res}]`
 | |
|         }
 | |
| 
 | |
|         let keys = Object.keys(value)
 | |
|         const keyLength = keys.length
 | |
|         if (keyLength === 0) {
 | |
|           return '{}'
 | |
|         }
 | |
|         if (maximumDepth < stack.length + 1) {
 | |
|           return '"[Object]"'
 | |
|         }
 | |
|         indentation += spacer
 | |
|         const join = `,\n${indentation}`
 | |
|         let res = ''
 | |
|         let separator = ''
 | |
|         let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth)
 | |
|         if (isTypedArrayWithEntries(value)) {
 | |
|           res += stringifyTypedArray(value, join, maximumBreadth)
 | |
|           keys = keys.slice(value.length)
 | |
|           maximumPropertiesToStringify -= value.length
 | |
|           separator = join
 | |
|         }
 | |
|         if (deterministic) {
 | |
|           keys = insertSort(keys)
 | |
|         }
 | |
|         stack.push(value)
 | |
|         for (let i = 0; i < maximumPropertiesToStringify; i++) {
 | |
|           const key = keys[i]
 | |
|           const tmp = stringifyIndent(key, value[key], stack, spacer, indentation)
 | |
|           if (tmp !== undefined) {
 | |
|             res += `${separator}"${strEscape(key)}": ${tmp}`
 | |
|             separator = join
 | |
|           }
 | |
|         }
 | |
|         if (keyLength > maximumBreadth) {
 | |
|           const removedKeys = keyLength - maximumBreadth
 | |
|           res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`
 | |
|           separator = join
 | |
|         }
 | |
|         if (separator !== '') {
 | |
|           res = `\n${indentation}${res}\n${originalIndentation}`
 | |
|         }
 | |
|         stack.pop()
 | |
|         return `{${res}}`
 | |
|       }
 | |
|       case 'number':
 | |
|         return isFinite(value) ? String(value) : fail ? fail(value) : 'null'
 | |
|       case 'boolean':
 | |
|         return value === true ? 'true' : 'false'
 | |
|       case 'undefined':
 | |
|         return undefined
 | |
|       case 'bigint':
 | |
|         if (bigint) {
 | |
|           return String(value)
 | |
|         }
 | |
|         // fallthrough
 | |
|       default:
 | |
|         return fail ? fail(value) : undefined
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function stringifySimple (key, value, stack) {
 | |
|     switch (typeof value) {
 | |
|       case 'string':
 | |
|         return `"${strEscape(value)}"`
 | |
|       case 'object': {
 | |
|         if (value === null) {
 | |
|           return 'null'
 | |
|         }
 | |
|         if (typeof value.toJSON === 'function') {
 | |
|           value = value.toJSON(key)
 | |
|           // Prevent calling `toJSON` again
 | |
|           if (typeof value !== 'object') {
 | |
|             return stringifySimple(key, value, stack)
 | |
|           }
 | |
|           if (value === null) {
 | |
|             return 'null'
 | |
|           }
 | |
|         }
 | |
|         if (stack.indexOf(value) !== -1) {
 | |
|           return circularValue
 | |
|         }
 | |
| 
 | |
|         let res = ''
 | |
| 
 | |
|         if (Array.isArray(value)) {
 | |
|           if (value.length === 0) {
 | |
|             return '[]'
 | |
|           }
 | |
|           if (maximumDepth < stack.length + 1) {
 | |
|             return '"[Array]"'
 | |
|           }
 | |
|           stack.push(value)
 | |
|           const maximumValuesToStringify = Math.min(value.length, maximumBreadth)
 | |
|           let i = 0
 | |
|           for (; i < maximumValuesToStringify - 1; i++) {
 | |
|             const tmp = stringifySimple(i, value[i], stack)
 | |
|             res += tmp !== undefined ? tmp : 'null'
 | |
|             res += ','
 | |
|           }
 | |
|           const tmp = stringifySimple(i, value[i], stack)
 | |
|           res += tmp !== undefined ? tmp : 'null'
 | |
|           if (value.length - 1 > maximumBreadth) {
 | |
|             const removedKeys = value.length - maximumBreadth - 1
 | |
|             res += `,"... ${getItemCount(removedKeys)} not stringified"`
 | |
|           }
 | |
|           stack.pop()
 | |
|           return `[${res}]`
 | |
|         }
 | |
| 
 | |
|         let keys = Object.keys(value)
 | |
|         const keyLength = keys.length
 | |
|         if (keyLength === 0) {
 | |
|           return '{}'
 | |
|         }
 | |
|         if (maximumDepth < stack.length + 1) {
 | |
|           return '"[Object]"'
 | |
|         }
 | |
|         let separator = ''
 | |
|         let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth)
 | |
|         if (isTypedArrayWithEntries(value)) {
 | |
|           res += stringifyTypedArray(value, ',', maximumBreadth)
 | |
|           keys = keys.slice(value.length)
 | |
|           maximumPropertiesToStringify -= value.length
 | |
|           separator = ','
 | |
|         }
 | |
|         if (deterministic) {
 | |
|           keys = insertSort(keys)
 | |
|         }
 | |
|         stack.push(value)
 | |
|         for (let i = 0; i < maximumPropertiesToStringify; i++) {
 | |
|           const key = keys[i]
 | |
|           const tmp = stringifySimple(key, value[key], stack)
 | |
|           if (tmp !== undefined) {
 | |
|             res += `${separator}"${strEscape(key)}":${tmp}`
 | |
|             separator = ','
 | |
|           }
 | |
|         }
 | |
|         if (keyLength > maximumBreadth) {
 | |
|           const removedKeys = keyLength - maximumBreadth
 | |
|           res += `${separator}"...":"${getItemCount(removedKeys)} not stringified"`
 | |
|         }
 | |
|         stack.pop()
 | |
|         return `{${res}}`
 | |
|       }
 | |
|       case 'number':
 | |
|         return isFinite(value) ? String(value) : fail ? fail(value) : 'null'
 | |
|       case 'boolean':
 | |
|         return value === true ? 'true' : 'false'
 | |
|       case 'undefined':
 | |
|         return undefined
 | |
|       case 'bigint':
 | |
|         if (bigint) {
 | |
|           return String(value)
 | |
|         }
 | |
|         // fallthrough
 | |
|       default:
 | |
|         return fail ? fail(value) : undefined
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function stringify (value, replacer, space) {
 | |
|     if (arguments.length > 1) {
 | |
|       let spacer = ''
 | |
|       if (typeof space === 'number') {
 | |
|         spacer = ' '.repeat(Math.min(space, 10))
 | |
|       } else if (typeof space === 'string') {
 | |
|         spacer = space.slice(0, 10)
 | |
|       }
 | |
|       if (replacer != null) {
 | |
|         if (typeof replacer === 'function') {
 | |
|           return stringifyFnReplacer('', { '': value }, [], replacer, spacer, '')
 | |
|         }
 | |
|         if (Array.isArray(replacer)) {
 | |
|           return stringifyArrayReplacer('', value, [], getUniqueReplacerSet(replacer), spacer, '')
 | |
|         }
 | |
|       }
 | |
|       if (spacer.length !== 0) {
 | |
|         return stringifyIndent('', value, [], spacer, '')
 | |
|       }
 | |
|     }
 | |
|     return stringifySimple('', value, [])
 | |
|   }
 | |
| 
 | |
|   return stringify
 | |
| }
 |