Socket
Socket
Sign inDemoInstall

safe-stable-stringify

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

safe-stable-stringify - npm Package Compare versions

Comparing version 1.1.1 to 2.0.0

.github/workflows/test.yml

3

benchmark.js

@@ -7,3 +7,4 @@ 'use strict'

const array = new Array(10).fill(0).map((_, i) => i)
// eslint-disable-next-line
const array = Array({ length: 10 }, (_, i) => i)
const obj = { array }

@@ -10,0 +11,0 @@ const circ = JSON.parse(JSON.stringify(obj))

# Changelog
## v2.0.0
- __[BREAKING]__ Convert BigInt to number by default instead of ignoring these values
If you wish to ignore these values similar to earlier versions, just use the new `bigint` option and set it to `false`.
- __[BREAKING]__ Support ESM
- __[BREAKING]__ Requires ES6
- Optional BigInt support
- Deterministic behavior is now optional
- The value to indicate a circular structure is now adjustable
- Significantly faster TypedArray stringification
- Smaller Codebase
- Removed stateful indentation to guarantee side-effect freeness
## v1.1.1

@@ -8,1 +21,9 @@

## v1.1.0
- Add support for IE11 (https://github.com/BridgeAR/safe-stable-stringify/commit/917b6128de135a950ec178d66d86b4d772c7656d)
- Fix issue with undefined values (https://github.com/BridgeAR/safe-stable-stringify/commit/4196f87, https://github.com/BridgeAR/safe-stable-stringify/commit/4eab558)
- Fix typescript definition (https://github.com/BridgeAR/safe-stable-stringify/commit/7a87478)
- Improve code coverage (https://github.com/BridgeAR/safe-stable-stringify/commit/ed8cadc, https://github.com/BridgeAR/safe-stable-stringify/commit/b58c494)
- Update dev dependencies (https://github.com/BridgeAR/safe-stable-stringify/commit/b857ea8)
- Improve docs
declare function stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string;
declare function stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
export interface StringifyOptions {
bigint?: boolean,
circularValue?: string | null,
deterministic?: boolean,
}
declare function configure(StringifyOptions): stringify;
stringify.configure = configure;
export default stringify;
'use strict'
const stringify = require('./stable')
const stringify = main()
stringify.configure = main
stringify.default = stringify
module.exports = stringify
stringify.default = stringify
// eslint-disable-next-line
const strEscapeSequencesRegExp = /[\x00-\x1f\x22\x5c]/
// eslint-disable-next-line
const strEscapeSequencesReplacer = /[\x00-\x1f\x22\x5c]/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) {
return meta[str.charCodeAt(0)]
}
// Escape control characters, double quotes and the backslash.
// Note: it is faster to run this only once for a big string instead of only for
// the parts that it is necessary for. But this is only true if we do not add
// extra indentation to the string before.
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
let i = 0
for (; i < str.length; i++) {
const point = str.charCodeAt(i)
if (point === 34 || point === 92 || point < 32) {
if (last === i) {
result += meta[point]
} else {
result += `${str.slice(last, i)}${meta[point]}`
}
last = i + 1
}
}
if (last === 0) {
result = str
} else if (last !== i) {
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 Uint8Array()
)
),
Symbol.toStringTag
).get
function isTypedArray (value) {
return typedArrayPrototypeGetSymbolToStringTag.call(value) !== undefined
}
function stringifyTypedArray (array, separator) {
if (array.length === 0) {
return ''
}
const whitespace = separator === ',' ? '' : ' '
let res = `"0":${whitespace}${array[0]}`
for (let i = 1; i < array.length; i++) {
res += `${separator}"${i}":${whitespace}${array[i]}`
}
return res
}
function getCircularValueOption (options) {
if (options && Object.prototype.hasOwnProperty.call(options, 'circularValue')) {
var circularValue = options.circularValue
if (typeof circularValue === 'string') {
circularValue = `"${circularValue}"`
} else if (circularValue !== null) {
throw new TypeError('The "circularValue" argument must be of type string or the value null')
}
}
return circularValue === undefined ? '"[Circular]"' : circularValue
}
function getBigIntOption (options) {
if (options && Object.prototype.hasOwnProperty.call(options, 'bigint')) {
var bigint = options.bigint
if (typeof bigint !== 'boolean') {
throw new TypeError('The "bigint" argument must be of type boolean')
}
}
return bigint === undefined ? true : bigint
}
function getDeterministicOption (options) {
if (options && Object.prototype.hasOwnProperty.call(options, 'deterministic')) {
var deterministic = options.deterministic
if (typeof deterministic !== 'boolean') {
throw new TypeError('The "deterministic" argument must be of type boolean')
}
}
return deterministic === undefined ? true : deterministic
}
function main (options) {
const circularValue = getCircularValueOption(options)
const bigint = getBigIntOption(options)
const deterministic = getDeterministicOption(options)
// Full version: supports all options
function stringifyFullFn (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 '[]'
}
stack.push(value)
if (spacer !== '') {
indentation += spacer
res += `\n${indentation}`
join = `,\n${indentation}`
}
let i = 0
for (; i < value.length - 1; i++) {
const tmp = stringifyFullFn(i, value, stack, replacer, spacer, indentation)
res += tmp !== undefined ? tmp : 'null'
res += join
}
const tmp = stringifyFullFn(i, value, stack, replacer, spacer, indentation)
res += tmp !== undefined ? tmp : 'null'
if (spacer !== '') {
res += `\n${originalIndentation}`
}
stack.pop()
return `[${res}]`
}
let keys = Object.keys(value)
if (keys.length === 0) {
return '{}'
}
let whitespace = ''
let separator = ''
if (spacer !== '') {
indentation += spacer
join = `,\n${indentation}`
whitespace = ' '
}
if (isTypedArray(value)) {
res += stringifyTypedArray(value, join)
keys = keys.slice(value.length)
separator = join
}
if (deterministic) {
keys = insertSort(keys)
}
stack.push(value)
for (const key of keys) {
const tmp = stringifyFullFn(key, value, 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) : 'null'
case 'boolean':
return value === true ? 'true' : 'false'
case 'bigint':
return bigint ? String(value) : undefined
}
}
function stringifyFullArr (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 '[]'
}
stack.push(value)
if (spacer !== '') {
indentation += spacer
res += `\n${indentation}`
join = `,\n${indentation}`
}
let i = 0
for (; i < value.length - 1; i++) {
const tmp = stringifyFullArr(i, value[i], stack, replacer, spacer, indentation)
res += tmp !== undefined ? tmp : 'null'
res += join
}
const tmp = stringifyFullArr(i, value[i], stack, replacer, spacer, indentation)
res += tmp !== undefined ? tmp : 'null'
if (spacer !== '') {
res += `\n${originalIndentation}`
}
stack.pop()
return `[${res}]`
}
if (replacer.length === 0) {
return '{}'
}
stack.push(value)
let whitespace = ''
if (spacer !== '') {
indentation += spacer
join = `,\n${indentation}`
whitespace = ' '
}
let separator = ''
for (const key of replacer) {
if (typeof key === 'string' || typeof key === 'number') {
const tmp = stringifyFullArr(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) : 'null'
case 'boolean':
return value === true ? 'true' : 'false'
case 'bigint':
return bigint ? String(value) : undefined
}
}
// Supports only the spacer option
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 '[]'
}
stack.push(value)
indentation += spacer
let res = `\n${indentation}`
const join = `,\n${indentation}`
let i = 0
for (; i < value.length - 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'
res += `\n${originalIndentation}`
stack.pop()
return `[${res}]`
}
let keys = Object.keys(value)
if (keys.length === 0) {
return '{}'
}
indentation += spacer
const join = `,\n${indentation}`
let res = ''
let separator = ''
if (isTypedArray(value)) {
res += stringifyTypedArray(value, join)
keys = keys.slice(value.length)
separator = join
}
if (deterministic) {
keys = insertSort(keys)
}
stack.push(value)
for (const key of keys) {
const tmp = stringifyIndent(key, value[key], stack, spacer, indentation)
if (tmp !== undefined) {
res += `${separator}"${strEscape(key)}": ${tmp}`
separator = join
}
}
if (separator !== '') {
res = `\n${indentation}${res}\n${originalIndentation}`
}
stack.pop()
return `{${res}}`
}
case 'number':
return isFinite(value) ? String(value) : 'null'
case 'boolean':
return value === true ? 'true' : 'false'
case 'bigint':
return bigint ? String(value) : undefined
}
}
// Simple without any options
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 '[]'
}
stack.push(value)
let i = 0
for (; i < value.length - 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'
stack.pop()
return `[${res}]`
}
let keys = Object.keys(value)
if (keys.length === 0) {
return '{}'
}
let separator = ''
if (isTypedArray(value)) {
res += stringifyTypedArray(value, ',')
keys = keys.slice(value.length)
}
if (deterministic) {
keys = insertSort(keys)
}
stack.push(value)
for (const key of keys) {
const tmp = stringifySimple(key, value[key], stack)
if (tmp !== undefined) {
res += `${separator}"${strEscape(key)}":${tmp}`
separator = ','
}
}
stack.pop()
return `{${res}}`
}
case 'number':
return isFinite(value) ? String(value) : 'null'
case 'boolean':
return value === true ? 'true' : 'false'
case 'bigint':
return bigint ? String(value) : undefined
}
}
function stringify (value, replacer, space) {
if (arguments.length > 1) {
let spacer = ''
if (typeof space === 'number') {
spacer = ' '.repeat(space)
} else if (typeof space === 'string') {
spacer = space
}
if (replacer != null) {
if (typeof replacer === 'function') {
return stringifyFullFn('', { '': value }, [], replacer, spacer, '')
}
if (Array.isArray(replacer)) {
return stringifyFullArr('', value, [], replacer, spacer, '')
}
}
if (spacer !== '') {
return stringifyIndent('', value, [], spacer, '')
}
}
return stringifySimple('', value, [])
}
return stringify
}
{
"name": "safe-stable-stringify",
"version": "1.1.1",
"version": "2.0.0",
"description": "Deterministic and safely JSON.stringify to quickly serialize JavaScript objects",
"exports": {
"require": "./index.js",
"import": "./esm/wrapper.js"
},
"keywords": [

@@ -40,4 +44,4 @@ "stable",

"json-stringify-safe": "^5.0.1",
"standard": "^14.3.3",
"tap": "^14.10.7"
"standard": "^15.0.0",
"tap": "^15.0.9"
},

@@ -51,4 +55,3 @@ "repository": {

},
"homepage": "https://github.com/BridgeAR/safe-stable-stringify#readme",
"dependencies": {}
"homepage": "https://github.com/BridgeAR/safe-stable-stringify#readme"
}
# safe-stable-stringify
[![Greenkeeper badge](https://badges.greenkeeper.io/BridgeAR/safe-stable-stringify.svg)](https://greenkeeper.io/)
Safe, deterministic and fast serialization alternative to [JSON.stringify][]. Zero dependencies. ESM and CJS. 100% coverage.
Safe, deterministic and fast serialization alternative to [JSON.stringify][].
Gracefully handles circular structures and bigint instead of throwing.
Gracefully handles circular structures instead of throwing.
Optional custom circular values and deterministic behavior.
## Usage
## stringify(value[, replacer[, space]])
The same as [JSON.stringify][].
`stringify(value[, replacer[, space]])`
* `value` {any}
* `replacer` {string[]|function|null}
* `space` {number|string}
* Returns: {string}
```js
const stringify = require('safe-stable-stringify')
const o = { b: 1, a: 0 }
o.o = o
console.log(stringify(o))
// '{"a":0,"b":1,"o":"[Circular]"}'
console.log(JSON.stringify(o))
const bigint = { a: 0, c: 2n, b: 1 }
stringify(bigint)
// '{"a":0,"b":1,"c":2}'
JSON.stringify(bigint)
// TypeError: Do not know how to serialize a BigInt
const circular = { b: 1, a: 0 }
circular.circular = circular
stringify(circular)
// '{"a":0,"b":1,"circular":"[Circular]"}'
JSON.stringify(circular)
// TypeError: Converting circular structure to JSON
function replacer(key, value) {
console.log('Key:', JSON.stringify(key))
// Remove the circular structure
if (key === 'o') {
return
}
return value
}
const serialized = stringify(o, replacer, 2)
// Key: ""
// Key: "a"
// Key: "b"
// Key: "o"
console.log(serialized)
stringify(circular, ['a', 'b'], 2)
// {

@@ -45,6 +43,47 @@ // "a": 0,

## stringify.configure(options)
* `bigint` {boolean} If `true`, bigint values are converted to a number. Otherwise
they are ignored. **Default:** `true`.
* `circularValue` {string|null} Define the value for circular references. **Default:** `[Circular]`.
* `deterministic` {boolean} If `true`, guarantee a deterministic key order
instead of relying on the insertion order. **Default:** `true`.
* Returns: {function} A stringify function with the options applied.
```js
import { configure } from 'safe-stable-stringify'
const stringify = configure({
bigint: true,
circularValue: 'Magic circle!',
deterministic: false,
})
const circular = {
bigint: 999_999_999_999_999_999n,
typed: new Uint8Array(3),
deterministic: "I don't think so",
}
circular.circular = circular
const stringified = stringify(circular, null, 4)
console.log(stringified)
// {
// "bigint": 999999999999999999,
// "typed": {
// "0": 0,
// "1": 0,
// "2": 0
// },
// "deterministic": "I don't think so",
// "circular": "Magic circle!"
// }
```
## Differences to JSON.stringify
1. replace circular structures with the string `[Circular]`
1. sorted keys instead of using the insertion order
1. Replace circular structures with the string `[Circular]` (the value may be changed).
1. Sorted keys instead of using the insertion order (it is possible to deactivate this).
1. BigInt values are stringified as regular number instead of throwing a TypeError.

@@ -58,3 +97,3 @@ Those are the only differences to the real JSON.stringify. This is a side effect

Currently this is by far the fastest known stable stringify implementation.
This is especially important for big objects.
This is especially important for big objects and TypedArrays.

@@ -98,3 +137,3 @@ (Lenovo T450s with a i7-5600U CPU using Node.js 8.9.4)

Sponsored by [nearForm](http://nearform.com)
Sponsored by [MaibornWolff](https://www.maibornwolff.de/) and [nearForm](http://nearform.com)

@@ -101,0 +140,0 @@ ## License

@@ -12,3 +12,3 @@ const { test } = require('tap')

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -24,3 +24,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -39,3 +39,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -54,3 +54,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -66,3 +66,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -87,3 +87,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -97,3 +97,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -113,3 +113,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -126,3 +126,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -136,3 +136,3 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -159,6 +159,6 @@ })

const actual = stringify(fixture)
assert.is(actual, expected)
assert.equal(actual, expected)
// check if the fixture has not been modified
assert.deepEqual(fixture, cloned)
assert.same(fixture, cloned)
assert.end()

@@ -183,4 +183,4 @@ })

// Making sure our original tests work
assert.deepEqual(parentObject.childObject.parentObject, parentObject)
assert.deepEqual(otherParentObject.otherChildObject.otherParentObject, otherParentObject)
assert.same(parentObject.childObject.parentObject, parentObject)
assert.same(otherParentObject.otherChildObject.otherParentObject, otherParentObject)

@@ -192,4 +192,4 @@ // Should both be idempotent

// Therefore the following assertion should be `true`
assert.deepEqual(parentObject.childObject.parentObject, parentObject)
assert.deepEqual(otherParentObject.otherChildObject.otherParentObject, otherParentObject)
assert.same(parentObject.childObject.parentObject, parentObject)
assert.same(otherParentObject.otherChildObject.otherParentObject, otherParentObject)

@@ -202,3 +202,3 @@ assert.end()

const actual = stringify(null)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -211,3 +211,3 @@ })

const actual = stringify(obj)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -220,3 +220,3 @@ })

const actual = stringify(obj)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -262,6 +262,16 @@ })

const actual = stringify(o)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()
})
test('invalid replacer being ignored', function (assert) {
const obj = { a: true }
const actual = stringify(obj, 'invalidReplacer')
const expected = stringify(obj)
assert.equal(actual, expected)
assert.end()
})
test('replacer removing elements', function (assert) {

@@ -272,10 +282,10 @@ const replacer = function (k, v) {

}
const obj = { f: null, remove: true }
const obj = { f: null, remove: true, typed: new Int32Array(1) }
const expected = JSON.stringify(obj, replacer)
let actual = stringify(obj, replacer)
assert.is(actual, expected)
assert.equal(actual, expected)
obj.obj = obj
actual = stringify(obj, replacer)
assert.is(actual, '{"f":null,"obj":"[Circular]"}')
assert.equal(actual, '{"f":null,"obj":"[Circular]","typed":{"0":0}}')

@@ -293,3 +303,3 @@ assert.end()

const actual = stringify(obj, replacer, 2)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -306,7 +316,7 @@ })

let actual = stringify(obj, replacer)
assert.is(actual, expected)
assert.equal(actual, expected)
expected = JSON.stringify({ toJSON () { return obj } }, replacer)
actual = stringify({ toJSON () { return obj } }, replacer)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -324,3 +334,3 @@ assert.end()

const actual = stringify(obj, replacer, 2)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -335,3 +345,3 @@ })

let actual = stringify(obj, replacer)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -341,3 +351,3 @@ obj.f = obj

actual = stringify({ toJSON () { return obj } }, replacer)
assert.is(actual, expected.replace('null', '"[Circular]"'))
assert.equal(actual, expected.replace('null', '"[Circular]"'))

@@ -353,3 +363,3 @@ assert.end()

const actual = stringify(obj, replacer)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -365,3 +375,3 @@ assert.end()

const actual = stringify(obj, replacer, 2)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -374,3 +384,3 @@ })

const actual = stringify(obj, null, 0)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -386,3 +396,3 @@ })

const actual = stringify(obj, replacer, ' ')
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -396,3 +406,3 @@ })

const actual = stringify(obj, replacer, ' ')
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -405,3 +415,3 @@ })

const actual = stringify(obj, undefined, 3)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -414,3 +424,3 @@ })

const actual = stringify(obj, undefined, 3)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -423,3 +433,3 @@ })

const actual = stringify(obj, (_, v) => v, 3)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -432,3 +442,3 @@ })

const actual = stringify(obj, (_, v) => v)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -441,3 +451,3 @@ })

const actual = stringify(obj, [false], 3)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -450,3 +460,3 @@ })

const actual = stringify(obj, [2])
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -459,3 +469,3 @@ })

const actual = stringify(obj, null, 5)
assert.is(actual, expected)
assert.equal(actual, expected)
assert.end()

@@ -469,3 +479,3 @@ })

let actual = stringify(obj)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -476,3 +486,3 @@ obj = { b: 'hello', a: undefined, c: 1 }

actual = stringify(obj)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -487,3 +497,3 @@ assert.end()

let actual = stringify(obj, null, 2)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -494,3 +504,3 @@ obj = { b: 'hello', a: undefined, c: 1 }

actual = stringify(obj)
assert.is(actual, expected)
assert.equal(actual, expected)

@@ -500,2 +510,153 @@ assert.end()

test('bigint option', function (assert) {
const stringifyNoBigInt = stringify.configure({ bigint: false })
const stringifyBigInt = stringify.configure({ bigint: true })
const obj = { a: 1n }
const actualBigInt = stringifyBigInt(obj, null, 1)
const actualNoBigInt = stringifyNoBigInt(obj, null, 1)
const actualDefault = stringify(obj, null, 1)
const expectedBigInt = '{\n "a": 1\n}'
const expectedNoBigInt = '{}'
assert.equal(actualNoBigInt, expectedNoBigInt)
assert.throws(() => JSON.stringify(obj, null, 1), TypeError)
assert.equal(actualBigInt, expectedBigInt)
assert.equal(actualDefault, expectedBigInt)
assert.throws(() => stringify.configure({ bigint: null }), /bigint/)
assert.end()
})
test('bigint option with replacer', function (assert) {
const stringifyBigInt = stringify.configure({ bigint: true })
const obj = { a: new BigUint64Array([1n]), 0: 1n }
const actualArrayReplacer = stringifyBigInt(obj, ['0', 'a'])
const actualFnReplacer = stringifyBigInt(obj, (k, v) => v)
const expected = '{"0":1,"a":{"0":1}}'
assert.equal(actualArrayReplacer, expected)
assert.equal(actualFnReplacer, expected)
assert.end()
})
test('bigint and typed array with indentation', function (assert) {
const obj = { a: 1n, t: new Int8Array(1) }
const expected = '{\n "a": 1,\n "t": {\n "0": 0\n }\n}'
const actual = stringify(obj, null, 1)
assert.equal(actual, expected)
assert.end()
})
test('bigint and typed array without indentation', function (assert) {
const obj = { a: 1n, t: new Int8Array(1) }
const expected = '{"a":1,"t":{"0":0}}'
const actual = stringify(obj, null, 0)
assert.equal(actual, expected)
assert.end()
})
test('no bigint without indentation', function (assert) {
const stringifyNoBigInt = stringify.configure({ bigint: false })
const obj = { a: 1n, t: new Int8Array(1) }
const expected = '{"t":{"0":0}}'
const actual = stringifyNoBigInt(obj, null, 0)
assert.equal(actual, expected)
assert.end()
})
test('circular value option', function (assert) {
let stringifyCircularValue = stringify.configure({ circularValue: 'YEAH!!!' })
const obj = {}
obj.circular = obj
const expected = '{"circular":"YEAH!!!"}'
const actual = stringifyCircularValue(obj)
assert.equal(actual, expected)
assert.equal(actual, expected)
assert.equal(stringify(obj), '{"circular":"[Circular]"}')
assert.throws(() => stringify.configure({ circularValue: { objects: 'are not allowed' } }), /circularValue/)
stringifyCircularValue = stringify.configure({ circularValue: null })
assert.equal(stringifyCircularValue(obj), '{"circular":null}')
assert.end()
})
test('non-deterministic', function (assert) {
const stringifyNonDeterministic = stringify.configure({ deterministic: false })
const obj = { b: true, a: false }
const expected = JSON.stringify(obj)
const actual = stringifyNonDeterministic(obj)
assert.equal(actual, expected)
assert.throws(() => stringify.configure({ deterministic: 1 }), /deterministic/)
assert.end()
})
test('non-deterministic with replacer', function (assert) {
const stringifyNonDeterministic = stringify.configure({ deterministic: false, bigint: false })
const obj = { b: true, a: 5n, c: Infinity, d: 4, e: [Symbol('null'), 5, Symbol('null')] }
const keys = Object.keys(obj)
const expected = stringify(obj, ['b', 'c', 'd', 'e'])
let actual = stringifyNonDeterministic(obj, keys)
assert.equal(actual, expected)
actual = stringifyNonDeterministic(obj, (k, v) => v)
assert.equal(actual, expected)
assert.end()
})
test('non-deterministic with indentation', function (assert) {
const stringifyNonDeterministic = stringify.configure({ deterministic: false, bigint: false })
const obj = { b: true, a: 5, c: Infinity, d: false, e: [Symbol('null'), 5, Symbol('null')] }
const expected = JSON.stringify(obj, null, 1)
const actual = stringifyNonDeterministic(obj, null, 1)
assert.equal(actual, expected)
assert.end()
})
test('check typed arrays', function (assert) {
const obj = [null, null, new Float32Array(99), Infinity, Symbol('null'), true, false, [], {}, Symbol('null')]
const expected = JSON.stringify(obj)
const actual = stringify(obj)
assert.equal(actual, expected)
assert.end()
})
test('check small typed arrays with extra properties', function (assert) {
const obj = new Uint8Array(0)
obj.foo = true
const expected = JSON.stringify(obj)
const actual = stringify(obj)
assert.equal(actual, expected)
assert.end()
})
test('trigger sorting fast path for objects with lots of properties', function (assert) {
const obj = {}
for (let i = 0; i < 1e4; i++) {
obj[`a${i}`] = i
}
const start = Date.now()
stringify(obj)
assert.ok(Date.now() - start < 100)
assert.end()
})
test('indent properly; regression test for issue #16', function (assert) {

@@ -522,15 +683,15 @@ const o = {

assert.is(
assert.equal(
stringify(o, null, 2),
indentedJSON
)
assert.is(
assert.equal(
stringify(o, arrayReplacer, 2),
indentedJSONArrayReplacer
)
assert.is(
assert.equal(
stringify(o, [], 2),
indentedJSONArrayEmpty
)
assert.is(
assert.equal(
stringify(o, (k, v) => v, 2),

@@ -545,11 +706,11 @@ indentedJSONReplacer

assert.is(
assert.equal(
stringify(o, arrayReplacer, 2),
indentedJSONArrayReplacer.replace(circularIdentifier, circularReplacement)
)
assert.is(
assert.equal(
stringify(o, null, 2),
indentedJSON.replace(circularIdentifier, circularReplacement)
)
assert.is(
assert.equal(
stringify(o, (k, v) => v, 2),

@@ -556,0 +717,0 @@ indentedJSONReplacer.replace(circularIdentifier, circularReplacement)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc