Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

safe-json-value

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

safe-json-value

JSON serialization should never fail

  • 1.0.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
2.3K
decreased by-41.56%
Maintainers
1
Weekly downloads
 
Created
Source

Codecov Node TypeScript Twitter Medium

JSON serialization should never fail.

Features

Prevent JSON.serialize() from:

Example

import safeJsonValue from 'safe-json-value'

const input = { one: true }
input.self = input

JSON.stringify(input) // Throws due to cycle
const { value, changes } = safeJsonValue(input)
JSON.stringify(value) // '{"one":true}"

console.log(changes) // List of changed properties
// [
//   {
//     path: ['self'],
//     oldValue: <ref *1> { one: true, self: [Circular *1] },
//     newValue: undefined,
//     reason: 'cycle'
//   }
// ]

Install

npm install safe-json-value

This package is an ES module and must be loaded using an import or import() statement, not require().

API

safeJsonValue(value, options?)

value any
options Options?
Return value: object

Makes value JSON-safe by:

Applied recursively on object/array properties. This never throws.

Options

Object with the following properties.

maxSize

Type: number
Default: Number.POSITIVE_INFINITY (no maximum size)

Maximum JSON.stringify(value).length. Additional properties beyond the size limit are omitted.

Return value

Object with the following properties.

value

Type: any

Copy of the input value after applying all the changes to make it JSON-safe.

The top-level value itself might be changed (including to undefined) if it is either invalid JSON or has a toJSON() method.

The value is not serialized to a JSON string. This allows choosing the serialization format (JSON, YAML, etc.), processing the value, etc.

changes

Type: Change[]

List of changes applied to value. Each item is an individual change to a specific property. A given property might have multiple changes, listed in order.

changes[*].path

Type: Array<string | symbol | number>

Property path.

It can be manipulated or stringified using wild-wild-parser.

changes[*].oldValue

Type: any

Property value before the change.

changes[*].newValue

Type: any

Property value after the change. undefined means the property was omitted.

changes[*].reason

Type: string

Reason for the change among: "bigint", "class", "cycle", "function", "getter", "infiniteNumber", "maxSize", "notArrayIndex", "notEnumerable", "notConfigurable", "notWritable", "symbolKey", "symbolValue", "toJSON", "uncaughtException", "undefined", "unsafeGetter" or "unsafeToJSON".

changes[*].error

Type: Error?

Error that triggered the change. Only present if reason is "uncaughtException", "unsafeGetter" or "unsafeToJSON".

Changes

This is a list of all possible changes applied to make the value JSON-safe.

Exceptions

JSON.stringify() can throw on specific properties. Those are omitted.

Cycles

const input = { one: true }
input.self = input
JSON.stringify(input) // Throws due to cycle
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"

Infinite recursion

const input = { toJSON: () => ({ one: true, input: { ...input } }) }
JSON.stringify(input) // Throws due to infinite `toJSON()` recursion
JSON.stringify(safeJsonValue(input).value) // '{"one":true,"input":{}}"

BigInt

const input = { one: true, two: 0n }
JSON.stringify(input) // Throws due to BigInt
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"

Exceptions in toJSON()

const input = {
  one: true,
  two: {
    toJSON() {
      throw new Error('example')
    },
  },
}
JSON.stringify(input) // Throws due to `toJSON()`
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"

Exceptions in getters

const input = {
  one: true,
  get two() {
    throw new Error('example')
  },
}
JSON.stringify(input) // Throws due to `get two()`
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"

Exceptions in proxies

const input = new Proxy(
  { one: false },
  {
    get() {
      throw new Error('example')
    },
  },
)
JSON.stringify(input) // Throws due to proxy
JSON.stringify(safeJsonValue(input).value) // '{}'

Non-writable properties

const input = {}
Object.defineProperty(input, 'one', {
  value: true,
  enumerable: true,
  writable: false,
  configurable: true,
})
input.one = false // Throws: non-writable
const safeInput = safeJsonValue(input).value
safeInput.one = false // Does not throw: now writable

Non-configurable properties

const input = {}
Object.defineProperty(input, 'one', {
  value: true,
  enumerable: true,
  writable: true,
  configurable: false,
})
// Throws: non-configurable
Object.defineProperty(input, 'one', { value: false, enumerable: false })
const safeInput = safeJsonValue(input).value
// Does not throw: now configurable
Object.defineProperty(safeInput, 'one', { value: false, enumerable: false })

Unexpected types

JSON.stringify() changes the types of specific values unexpectedly. Those are omitted.

NaN and Infinity

const input = { one: true, two: Number.NaN, three: Number.POSITIVE_INFINITY }
JSON.stringify(input) // '{"one":true,"two":null,"three":null}"
JSON.stringify(safeJsonValue(input).value) // '{"one":true}"

Invalid array items

const input = [true, undefined, Symbol(), false]
JSON.stringify(input) // '[true, null, null, false]'
JSON.stringify(safeJsonValue(input).value) // '[true, false]'

Filtered values

JSON.stringify() omits some specific types. Those are omitted right away to prevent any unexpected output.

Functions

const input = { one: true, two() {} }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

undefined

const input = { one: true, two: undefined }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Symbol values

const input = { one: true, two: Symbol() }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Symbol keys

const input = { one: true, [Symbol()]: true }
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Non-enumerable keys

const input = { one: true }
Object.defineProperty(input, 'two', { value: true, enumerable: false })
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Array properties

const input = [true]
input.prop = true
JSON.parse(JSON.stringify(input)) // [true]
safeJsonValue(input).value // [true]

Unresolved values

JSON.stringify() can transform some values. Those are resolved right away to prevent any unexpected output.

toJSON()

const input = {
  toJSON() {
    return { one: true }
  },
}
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Dates

const input = { one: new Date() }
JSON.parse(JSON.stringify(input)) // { one: '2022-07-29T14:37:40.865Z' }
safeJsonValue(input).value // { one: '2022-07-29T14:37:40.865Z' }

Classes

const input = { one: new Set([]) }
JSON.parse(JSON.stringify(input)) // { one: {} }
safeJsonValue(input).value // { one: {} }

Getters

const input = {
  get one() {
    return true
  },
}
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Proxies

const input = new Proxy(
  { one: false },
  {
    get() {
      return true
    },
  },
)
JSON.parse(JSON.stringify(input)) // { one: true }
safeJsonValue(input).value // { one: true }

Big output

Big JSON strings can make a process, filesystem operation or network request crash.

When using the maxSize option, properties that are too large are omitted. Large values (including strings) are completely removed, not truncated.

const input = { one: true, two: 'a'.repeat(1e6) }
JSON.stringify(safeJsonValue(input, { maxSize: 1e5 }).value) // '{"one":true}"

Support

For any question, don't hesitate to submit an issue on GitHub.

Everyone is welcome regardless of personal background. We enforce a Code of conduct in order to promote a positive and inclusive environment.

Contributing

This project was made with ❤️. The simplest way to give back is by starring and sharing it online.

If the documentation is unclear or has a typo, please click on the page's Edit button (pencil icon) and suggest a correction.

If you would like to help us fix a bug or add a new feature, please check our guidelines. Pull requests are welcome!

FAQs

Package last updated on 29 Jul 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

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