• Stars
    star
    198
  • Rank 196,898 (Top 4 %)
  • Language
    JavaScript
  • License
    Apache License 2.0
  • Created over 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

⛑️ JSON serialization should never fail
modern-errors logo

Node Browsers TypeScript Codecov Minified size Mastodon Medium

⛑️ JSON serialization should never fail.

Features

Prevent JSON.stringify() 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 works in both Node.js >=16.17.0 and browsers.

This is an ES module. It must be loaded using an import or import() statement, not require(). If TypeScript is used, it must be configured to output ES modules, not CommonJS.

API

safeJsonValue(value, options?)

value any
options Options?
Return value: object

Makes value JSON-safe by:

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

shallow

Type: boolean
Default: false

If false, object/array properties are processed recursively. Please note that cycles are not removed when this is 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: () => ({ 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: () => 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!


ehmicky

💻 🎨 🤔 📖

Pedro Augusto de Paula Barbosa

📖

More Repositories

1

cross-platform-node-guide

📗 How to write cross-platform Node.js code
JavaScript
1,331
star
2

modern-errors

Handle errors in a simple, stable, consistent way
JavaScript
1,273
star
3

nve

Run any command on specific Node.js versions
JavaScript
614
star
4

wild-wild-path

🤠 Object property paths with wildcards and regexps 🌵
JavaScript
608
star
5

log-process-errors

Show some ❤️ to Node.js process errors
JavaScript
471
star
6

human-signals

Human-friendly process signals
JavaScript
261
star
7

autoserver

Create a full-featured REST/GraphQL API from a configuration file
JavaScript
200
star
8

cross-platform-terminal-characters

All the characters that work on most terminals
JavaScript
196
star
9

unix-permissions

Swiss Army knife for Unix permissions
JavaScript
119
star
10

test-each

🤖 Repeat tests. Repeat tests. Repeat tests.
JavaScript
103
star
11

Notes

Technologies I've learned
sed
58
star
12

gulp-execa

Gulp.js command execution for humans
JavaScript
55
star
13

fast-cartesian

Fast cartesian product
TypeScript
53
star
14

get-bin-path

Get the current package's binary path
JavaScript
34
star
15

wild-wild-utils

🤠 Functional utilities using object property paths with wildcards and regexps 🌵
JavaScript
28
star
16

get-node

Download a specific version of Node.js
JavaScript
19
star
17

handle-cli-error

💣 Error handler for CLI applications 💥
JavaScript
15
star
18

keep-func-props

Wrap a function without changing its name and other properties
JavaScript
13
star
19

node-version-alias

Resolve Node.js version aliases like `latest`, `lts` or `erbium`
JavaScript
12
star
20

big-cartesian

Cartesian product for big inputs
TypeScript
12
star
21

eslint-config

ESLint configuration for my own projects
JavaScript
10
star
22

preferred-node-version

Get the preferred Node.js version of a project or user
JavaScript
9
star
23

all-node-versions

List all available Node.js versions
JavaScript
9
star
24

error-serializer

Convert errors to/from plain objects.
JavaScript
9
star
25

modern-errors-http

`modern-errors` plugin to create HTTP error responses.
TypeScript
8
star
26

normalize-node-version

Normalize and validate Node.js versions
JavaScript
8
star
27

cv-website

Static page with my CV website
HTML
8
star
28

truncate-json

Truncate a JSON string.
JavaScript
8
star
29

merge-error-cause

Merge an error with its `cause`
JavaScript
7
star
30

get-node-cli

Download a specific version of Node.js (CLI)
JavaScript
7
star
31

global-cache-dir

Get the global cache directory
JavaScript
6
star
32

dev-tasks

Automated development tasks for my own projects
Shell
5
star
33

modern-errors-serialize

`modern-errors` plugin to serialize/parse errors.
JavaScript
5
star
34

portuguese-conjugation-cheat-sheet

Portuguese conjugation cheat sheet
CSS
5
star
35

spyd

Complete yet simple benchmark runner
JavaScript
5
star
36

string-byte-length

Get the UTF-8 byte length of a string.
JavaScript
5
star
37

template-javascript

JavaScript library template
JavaScript
5
star
38

colors-option

Let users toggle colors
JavaScript
5
star
39

modern-errors-winston

`modern-errors` plugin for Winston.
JavaScript
5
star
40

normalize-exception

Normalize exceptions/errors
JavaScript
5
star
41

abstract-parser

Abstraction layer for JavaScript parsers
JavaScript
4
star
42

modern-errors-cli

`modern-errors` plugin to handle errors in CLI modules.
TypeScript
4
star
43

error-custom-class

Create custom error classes
JavaScript
4
star
44

fetch-node-website

Fetch releases on nodejs.org
JavaScript
4
star
45

winston-error-format

Log errors with Winston
JavaScript
4
star
46

is-json-value

Check if a value is valid JSON.
JavaScript
4
star
47

error-cause-polyfill

Polyfill `error.cause`
JavaScript
4
star
48

modern-errors-switch

`modern-errors` plugin to execute class-specific logic.
JavaScript
4
star
49

modern-errors-clean

`modern-errors` plugin to clean stack traces.
JavaScript
4
star
50

wild-wild-parser

🤠 Parser for object property paths with wildcards and regexps 🌵
JavaScript
4
star
51

precise-now

Like `performance.now()` but in nanoseconds
TypeScript
4
star
52

error-http-response

Create HTTP error responses.
JavaScript
3
star
53

test-api

[WIP] Automated API testing
JavaScript
3
star
54

modern-errors-process

`modern-errors` plugin to handle process errors.
JavaScript
3
star
55

guess-json-indent

Guess the indentation of a JSON string.
JavaScript
3
star
56

time-resolution

Find the process's time resolution
TypeScript
3
star
57

is-error-instance

Check if a value is an `Error` instance.
TypeScript
3
star
58

declarative-merge

Merge objects/arrays declaratively
JavaScript
3
star
59

template-typescript

TypeScript library template
TypeScript
3
star
60

modern-errors-bugs

`modern-errors` plugin to print where to report bugs.
JavaScript
3
star
61

set-error-class

Properly update an error's class.
JavaScript
2
star
62

dev-parser

Parse JavaScript using a terminal
JavaScript
2
star
63

ehmicky

Node.js back-end developer
2
star
64

set-error-stack

Properly update an error's stack.
JavaScript
2
star
65

set-array

Set array items declaratively
JavaScript
2
star
66

set-error-message

Properly update an error's message.
JavaScript
2
star
67

set-error-props

Properly update an error's properties
JavaScript
2
star
68

string-byte-slice

Like `string.slice()` but bytewise.
JavaScript
2
star
69

redefine-property

Better `Object.defineProperty()`
JavaScript
2
star
70

terminal-theme

🎨 Use a color theme for your code's terminal output
JavaScript
2
star
71

wrap-error-message

Properly wrap an error's message.
JavaScript
2
star
72

chalk-string

Chalk with style strings.
JavaScript
2
star
73

error-class-utils

Properly create error classes.
JavaScript
2
star
74

create-error-types

Create multiple error types.
JavaScript
1
star
75

oh-oh

This is an.
JavaScript
1
star
76

design

Logos of my projects
1
star
77

prettier-config

Prettier configuration for my own projects
JavaScript
1
star