safe-stable-stringify
Advanced tools
Comparing version 2.3.1 to 2.4.0
@@ -10,2 +10,3 @@ export function stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; | ||
maximumDepth?: number, | ||
strict?: boolean, | ||
} | ||
@@ -12,0 +13,0 @@ |
100
index.js
'use strict' | ||
const { hasOwnProperty } = Object.prototype | ||
const stringify = configure() | ||
@@ -23,3 +25,3 @@ | ||
// eslint-disable-next-line | ||
const strEscapeSequencesReplacer = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/g | ||
const strEscapeSequencesReplacer = new RegExp(strEscapeSequencesRegExp, 'g') | ||
@@ -73,4 +75,4 @@ // Escaped special characters. Use empty strings to fill up unused entries. | ||
if (point <= 0xdbff && i + 1 < str.length) { | ||
const point = str.charCodeAt(i + 1) | ||
if (point >= 0xdc00 && point <= 0xdfff) { | ||
const nextPoint = str.charCodeAt(i + 1) | ||
if (nextPoint >= 0xdc00 && nextPoint <= 0xdfff) { | ||
i++ | ||
@@ -80,3 +82,3 @@ continue | ||
} | ||
result += `${str.slice(last, i)}${`\\u${point.toString(16)}`}` | ||
result += `${str.slice(last, i)}\\u${point.toString(16)}` | ||
last = i + 1 | ||
@@ -111,3 +113,3 @@ } | ||
Object.getPrototypeOf( | ||
new Uint8Array() | ||
new Int8Array() | ||
) | ||
@@ -135,4 +137,4 @@ ), | ||
function getCircularValueOption (options) { | ||
if (options && Object.prototype.hasOwnProperty.call(options, 'circularValue')) { | ||
var circularValue = options.circularValue | ||
if (options && hasOwnProperty.call(options, 'circularValue')) { | ||
const circularValue = options.circularValue | ||
if (typeof circularValue === 'string') { | ||
@@ -157,4 +159,5 @@ return `"${circularValue}"` | ||
function getBooleanOption (options, key) { | ||
if (options && Object.prototype.hasOwnProperty.call(options, key)) { | ||
var value = options[key] | ||
let value | ||
if (options && hasOwnProperty.call(options, key)) { | ||
value = options[key] | ||
if (typeof value !== 'boolean') { | ||
@@ -168,4 +171,5 @@ throw new TypeError(`The "${key}" argument must be of type boolean`) | ||
function getPositiveIntegerOption (options, key) { | ||
if (options && Object.prototype.hasOwnProperty.call(options, key)) { | ||
var value = options[key] | ||
let value | ||
if (options && hasOwnProperty.call(options, key)) { | ||
value = options[key] | ||
if (typeof value !== 'number') { | ||
@@ -194,5 +198,3 @@ throw new TypeError(`The "${key}" argument must be of type number`) | ||
for (const value of replacerArray) { | ||
if (typeof value === 'string') { | ||
replacerSet.add(value) | ||
} else if (typeof value === 'number') { | ||
if (typeof value === 'string' || typeof value === 'number') { | ||
replacerSet.add(String(value)) | ||
@@ -204,3 +206,29 @@ } | ||
function getStrictOption (options) { | ||
if (options && 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) | ||
@@ -314,7 +342,14 @@ const bigint = getBooleanOption(options, 'bigint') | ||
case 'number': | ||
return isFinite(value) ? String(value) : 'null' | ||
return isFinite(value) ? String(value) : fail ? fail(value) : 'null' | ||
case 'boolean': | ||
return value === true ? 'true' : 'false' | ||
case 'undefined': | ||
return undefined | ||
case 'bigint': | ||
return bigint ? String(value) : undefined | ||
if (bigint) { | ||
return String(value) | ||
} | ||
// fallthrough | ||
default: | ||
return fail ? fail(value) : undefined | ||
} | ||
@@ -400,7 +435,14 @@ } | ||
case 'number': | ||
return isFinite(value) ? String(value) : 'null' | ||
return isFinite(value) ? String(value) : fail ? fail(value) : 'null' | ||
case 'boolean': | ||
return value === true ? 'true' : 'false' | ||
case 'undefined': | ||
return undefined | ||
case 'bigint': | ||
return bigint ? String(value) : undefined | ||
if (bigint) { | ||
return String(value) | ||
} | ||
// fallthrough | ||
default: | ||
return fail ? fail(value) : undefined | ||
} | ||
@@ -504,7 +546,14 @@ } | ||
case 'number': | ||
return isFinite(value) ? String(value) : 'null' | ||
return isFinite(value) ? String(value) : fail ? fail(value) : 'null' | ||
case 'boolean': | ||
return value === true ? 'true' : 'false' | ||
case 'undefined': | ||
return undefined | ||
case 'bigint': | ||
return bigint ? String(value) : undefined | ||
if (bigint) { | ||
return String(value) | ||
} | ||
// fallthrough | ||
default: | ||
return fail ? fail(value) : undefined | ||
} | ||
@@ -598,7 +647,14 @@ } | ||
case 'number': | ||
return isFinite(value) ? String(value) : 'null' | ||
return isFinite(value) ? String(value) : fail ? fail(value) : 'null' | ||
case 'boolean': | ||
return value === true ? 'true' : 'false' | ||
case 'undefined': | ||
return undefined | ||
case 'bigint': | ||
return bigint ? String(value) : undefined | ||
if (bigint) { | ||
return String(value) | ||
} | ||
// fallthrough | ||
default: | ||
return fail ? fail(value) : undefined | ||
} | ||
@@ -605,0 +661,0 @@ } |
{ | ||
"name": "safe-stable-stringify", | ||
"version": "2.3.1", | ||
"version": "2.4.0", | ||
"description": "Deterministic and safely JSON.stringify to quickly serialize JavaScript objects", | ||
@@ -41,3 +41,3 @@ "exports": { | ||
"devDependencies": { | ||
"@types/json-stable-stringify": "^1.0.32", | ||
"@types/json-stable-stringify": "^1.0.34", | ||
"@types/node": "^16.11.1", | ||
@@ -47,3 +47,3 @@ "benchmark": "^2.1.4", | ||
"fast-json-stable-stringify": "^2.1.0", | ||
"fast-safe-stringify": "^2.0.7", | ||
"fast-safe-stringify": "^2.1.1", | ||
"fast-stable-stringify": "^1.0.0", | ||
@@ -53,7 +53,7 @@ "faster-stable-stringify": "^1.0.0", | ||
"json-stable-stringify": "^1.0.1", | ||
"json-stringify-deterministic": "^1.0.1", | ||
"json-stringify-deterministic": "^1.0.7", | ||
"json-stringify-safe": "^5.0.1", | ||
"standard": "^15.0.0", | ||
"standard": "^16.0.4", | ||
"tap": "^15.0.9", | ||
"typescript": "^4.4.3" | ||
"typescript": "^4.8.3" | ||
}, | ||
@@ -60,0 +60,0 @@ "repository": { |
# safe-stable-stringify | ||
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][]. | ||
Zero dependencies. ESM and CJS. 100% coverage. | ||
Gracefully handles circular structures and bigint instead of throwing. | ||
Optional custom circular values and deterministic behavior. | ||
Optional custom circular values, deterministic behavior or strict JSON | ||
compatibility check. | ||
@@ -50,3 +52,3 @@ ## stringify(value[, replacer[, space]]) | ||
serialized (array entries are replaced with `null`). Set to `Error`, to throw | ||
on circular references. **Default:** `[Circular]`. | ||
on circular references. **Default:** `'[Circular]'`. | ||
* `deterministic` {boolean} If `true`, guarantee a deterministic key order | ||
@@ -62,2 +64,6 @@ instead of relying on the insertion order. **Default:** `true`. | ||
`'[Object]'` and arrays as `'[Array]'`. **Default:** `Infinity` | ||
* `strict` {boolean} Instead of handling any JSON value gracefully, throw an | ||
error in case it may not be represented as JSON (functions, NaN, ...). | ||
Circular values and bigint values throw as well in case either option is not | ||
explicitly defined. Sets and Maps are not detected! **Default:** `false` | ||
* Returns: {function} A stringify function with the options applied. | ||
@@ -106,5 +112,6 @@ | ||
1. _Circular values_ are replaced with the string `[Circular]` (the value may be changed). | ||
1. _Object keys_ are sorted 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. | ||
1. _Circular values_ are replaced with the string `[Circular]` (configurable). | ||
1. _Object keys_ are sorted instead of using the insertion order (configurable). | ||
1. _BigInt_ values are stringified as regular number instead of throwing a | ||
TypeError (configurable). | ||
1. _Boxed primitives_ (e.g., `Number(5)`) are not unboxed and are handled as | ||
@@ -111,0 +118,0 @@ regular object. |
668
178
32297
8