⛑️ JSON serialization should never fail

Overview
modern-errors logo

Codecov TypeScript Node 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: 'unsafeCycle'
//   }
// ]

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: 1e7

Big JSON strings can make a process, filesystem operation or network request crash. maxSize prevents it by setting a maximum JSON.stringify(value).length.

Additional properties beyond the size limit are omitted. They are completely removed, not truncated (including strings).

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

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.

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:

changes[*].error

Type: Error?

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

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 }) }
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}"

Big output

const input = { one: true, two: '\n'.repeat(5e8) }
JSON.stringify(input) // Throws due to max string length
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) // '{}'

Invalid descriptors

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 }

Related projects

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!

You might also like...

This repository contains an Advanced Zoom Apps Sample. It should serve as a starting point for you to build and test your own Zoom App in development.

Advanced Zoom Apps Sample Advanced Sample covers most complex scenarios that you might be needed in apps. App has reference implementation for: Authen

Dec 17, 2022

How often do you get asked about the gadgets or software that you use? If the answer is quite often, you should be trying show off out. Curate the list of gadgets and software and share it with your fans and followers.

How often do you get asked about the gadgets or software that you use? If the answer is quite often, you should be trying show off out. Curate the list of gadgets and software and share it with your fans and followers.

Show Off - Showcase your setup! How often do you get asked about the gadgets or software that you use? If the answer is quite often, you should be try

Nov 24, 2022

Please do not use this tracker to scam anyone! This is free and will be forever free. This tracking will never ask for seed phrases nor private keys. Keep safe!

CryptoBlades Tracker Related modules express - web application framework for node pug - template engine stylus - pre-processor CSS mongoose - nodejs o

Oct 13, 2022

A community-centric site like you've never seen before.

Kleptonix A community-centric site like you've never seen before. Overview This section will be updated when basic posting and account creation functi

Apr 19, 2022

Move all the disks from the left hand post to the right hand post, only moving the disks one at a time and a bigger disk can never be placed on a smaller disk.

Move all the disks from the left hand post to the right hand post, only moving the disks one at a time and a bigger disk can never be placed on a smaller disk.

Hanoi Tower Description The Tower of Hanoi was a famous problem posed by a mathematician in 1883, The "puzzle" is to move all the disks from the left

Feb 5, 2022

Timers for Lost Ark bosses, islands, events, wandering merchants and more! Never miss an event again.

Timers for Lost Ark bosses, islands, events, wandering merchants and more! Never miss an event again.

Timers for Lost Ark bosses, islands, events, wandering merchants and more! Never miss an event again. LostArkTimer.app Website Website Features Event

Oct 17, 2022

And idea that never worked but was fun while it lasted

THIS IDEA WAS FUN BUT IS DEAD NOW AND SHOULD NOT BE TRUSTED FOR ANYTHING!!! Authsio Core Core authsio web application for identity & access management

Oct 20, 2022

Read without losing the plot. Well Read helps you organize your notes about books you're reading, so you're never lost when starting a new volume.

Read without losing the plot. Well Read helps you organize your notes about books you're reading, so you're never lost when starting a new volume.

Well Read Well Read is a website for tracking your reading of long book series. I made this to track how many pages I read in a session and to better

Dec 15, 2022

A web app which help you to save you a list of your favorite books, they will be saved on your local storage to never loose them even if you close the page. Built wiht JavaScript

Awesome Books In this project I build a page to save a list of your favorites books, you can add new books, delete it and they will be saved in the lo

Dec 17, 2022
Comments
  • Bump decode-uri-component from 0.2.0 to 0.2.2

    Bump decode-uri-component from 0.2.0 to 0.2.2

    Bumps decode-uri-component from 0.2.0 to 0.2.2.

    Release notes

    Sourced from decode-uri-component's releases.

    v0.2.2

    • Prevent overwriting previously decoded tokens 980e0bf

    https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.1...v0.2.2

    v0.2.1

    • Switch to GitHub workflows 76abc93
    • Fix issue where decode throws - fixes #6 746ca5d
    • Update license (#1) 486d7e2
    • Tidelift tasks a650457
    • Meta tweaks 66e1c28

    https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.1

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
Releases(1.11.0)
Owner
ehmicky
Node.js back-end developer
ehmicky
Serialization library for data-oriented design structures in JavaScript

Data-oriented Serialization for SoA/AoA A zero-dependency serialization library for data-oriented design structures like SoA (Structure of Arrays) and

null 11 Sep 27, 2022
Binary-encoded serialization of JavaScript objects with generator-based parser and serializer

YaBSON Schemaless binary-encoded serialization of JavaScript objects with generator-based parser and serializer This library is designed to transfer l

Gildas 11 Aug 9, 2022
superserial provides serialization in any way you can imagine.

superserial After data transfer, when the object needs to be restored, JSON has many limitations. It does not support values such as Infinity and NaN,

DenoStack 24 Dec 23, 2022
A RESP 'Redis Serialization Protocol' library implementation to generate a server, uses a similar approach to express to define you serer, making it easy and fast.

RESPRESS A RESP 'Redis Serialization Protocol' library implementation to generate a server, uses a similar approach to express to define you serer, ma

Yousef Wadi 9 Aug 29, 2022
Glorious Binary Serialization and Deserialization for TypeScript.

Glorious SerDes for TypeScript The library you can rely on, For binary serialization and deserialization, In Node, Deno, and the Web environment, Whic

Wei 25 Dec 11, 2022
Serialize arbitrary NodeJS closures and customize serialization behavior.

Closure Serializer This is a fork of the Pulumi Closure Serializer. @pulumi/pulumi. Motivation Functionless allows developers to write cloud applicati

null 4 Jul 19, 2022
JCS (JSON Canonicalization Scheme), JSON digests, and JSON Merkle hashes

JSON Hash This package contains the following JSON utilties for Deno: digest.ts provides cryptographic hash digests of JSON trees. It guarantee that d

Hong Minhee (洪 民憙) 13 Sep 2, 2022
Package fetcher is a bot messenger which gather npm packages by uploading either a json file (package.json) or a picture representing package.json. To continue...

package-fetcher Ce projet contient un boilerplate pour un bot messenger et l'executable Windows ngrok qui va permettre de créer un tunnel https pour c

AILI Fida Aliotti Christino 2 Mar 29, 2022
A collection of (mostly) technical things every software developer should know about

Join our community for professional Software Developers and get more control over your life and career! Every Programmer Should Know ?? A collection o

MTDV 66.6k Jan 4, 2023
📜 33 concepts every JavaScript developer should know.

33 Concepts Every JavaScript Developer Should Know Introduction This repository was created with the intention of helping developers master their conc

Leonardo Maldonado 53.9k Dec 31, 2022