UNPKG

shell-mirror

Version:

Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.

626 lines (586 loc) 19.9 kB
'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]/ // 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 }