Buckets:
| 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]/ | |
| // 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}"` | |
| } | |
| return JSON.stringify(str) | |
| } | |
| function sort (array, comparator) { | |
| // 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 || comparator) { | |
| return array.sort(comparator) | |
| } | |
| 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 getDeterministicOption (options) { | |
| let value | |
| if (hasOwnProperty.call(options, 'deterministic')) { | |
| value = options.deterministic | |
| if (typeof value !== 'boolean' && typeof value !== 'function') { | |
| throw new TypeError('The "deterministic" argument must be of type boolean or comparator function') | |
| } | |
| } | |
| return value === undefined ? true : value | |
| } | |
| 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 = getDeterministicOption(options) | |
| const comparator = typeof deterministic === 'function' ? deterministic : undefined | |
| 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(String(i), value, stack, replacer, spacer, indentation) | |
| res += tmp !== undefined ? tmp : 'null' | |
| res += join | |
| } | |
| const tmp = stringifyFnReplacer(String(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 = ' ' | |
| } | |
| const maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth) | |
| if (deterministic && !isTypedArrayWithEntries(value)) { | |
| keys = sort(keys, comparator) | |
| } | |
| 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(String(i), value[i], stack, replacer, spacer, indentation) | |
| res += tmp !== undefined ? tmp : 'null' | |
| res += join | |
| } | |
| const tmp = stringifyArrayReplacer(String(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(String(i), value[i], stack, spacer, indentation) | |
| res += tmp !== undefined ? tmp : 'null' | |
| res += join | |
| } | |
| const tmp = stringifyIndent(String(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 = sort(keys, comparator) | |
| } | |
| 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 = '' | |
| const hasLength = value.length !== undefined | |
| if (hasLength && 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(String(i), value[i], stack) | |
| res += tmp !== undefined ? tmp : 'null' | |
| res += ',' | |
| } | |
| const tmp = stringifySimple(String(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 (hasLength && isTypedArrayWithEntries(value)) { | |
| res += stringifyTypedArray(value, ',', maximumBreadth) | |
| keys = keys.slice(value.length) | |
| maximumPropertiesToStringify -= value.length | |
| separator = ',' | |
| } | |
| if (deterministic) { | |
| keys = sort(keys, comparator) | |
| } | |
| 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 | |
| } | |
Xet Storage Details
- Size:
- 19.9 kB
- Xet hash:
- a92872b244c82ecc944cdc1febbe485174f7949819abd62a5ce669b0787ad91d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.