├── .eslintignore
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmrc
├── .zuul.yml
├── LICENSE
├── Makefile
├── README.md
├── package.json
├── src
├── index.js
└── internal
│ ├── reference.js
│ └── utils.js
└── test
└── index.test.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage/*
2 | tmp
3 | lib
4 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: []
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [14.x, 16.x, 17.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v2
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | # cache: 'npm'
29 | - run: npm install --ignore-scripts
30 | - run: npm run ci
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage/
3 | .nyc_output/
4 | doc/
5 | tmp/
6 | lib/
7 | *.log
8 | *.tgz
9 | *.sh
10 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.zuul.yml:
--------------------------------------------------------------------------------
1 | ui: mocha-bdd
2 | # tunnel_host: http://focusaurus.com
3 | browsers:
4 | - name: chrome
5 | platform: Windows 10
6 | version:
7 | - 69
8 | - latest
9 | - name: firefox
10 | platform: Windows 10
11 | version:
12 | - 60
13 | - latest
14 | - name: safari
15 | version:
16 | - 11
17 | - latest
18 | - name: MicrosoftEdge
19 | version:
20 | - 17
21 | - latest
22 | # - name: iphone
23 | # version: latest
24 | # Not Supported
25 | # - name: internet explorer
26 | # version: 11
27 | # platform: Windows 10
28 | # - name: internet explorer
29 | # version: 9..latest
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-present commenthol
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: readme v8 v10 v12 v13
2 |
3 | readme: README.md
4 | markedpp --githubid -i $< -o $<
5 |
6 | v%:
7 | n $@ && npm test
8 |
9 | zuul:
10 | node_modules/.bin/zuul --local 3000 test/*.js
11 |
12 | .PHONY: all readme zuul
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # serialize-to-js
2 |
3 | > serialize objects to javascript
4 |
5 | [](https://www.npmjs.com/package/serialize-to-js/)
6 | [](https://github.com/commenthol/serialize-to-js/actions/workflows/ci.yml?query=branch%3Amaster)
7 |
8 |
9 | Serialize objects into a string while checking circular structures and respecting references.
10 |
11 | The following Objects are supported
12 |
13 | - String
14 | - Number
15 | - Boolean
16 | - Object
17 | - Array
18 | - RegExp
19 | - Error
20 | - Date
21 | - Buffer
22 | - Int8Array, Uint8Array, Uint8ClampedArray
23 | - Int16Array, Uint16Array
24 | - Int32Array, Uint32Array, Float32Array
25 | - Float64Array
26 | - Set
27 | - Map
28 |
29 | ## Table of Contents
30 |
31 |
32 |
33 | * [Methods](#methods)
34 | * [serialize](#serialize)
35 | * [Contribution and License Agreement](#contribution-and-license-agreement)
36 | * [License](#license)
37 |
38 |
39 |
40 | ## Methods
41 |
42 | ### serialize
43 |
44 | `serialize(source, opts, opts.ignoreCircular, opts.reference)`
45 |
46 | serializes an object to javascript
47 |
48 | #### Example - serializing regex, date, buffer, ...
49 |
50 | ```js
51 | const serialize = require('serialize-to-js')
52 | const obj = {
53 | str: '',
54 | num: 3.1415,
55 | bool: true,
56 | nil: null,
57 | undef: undefined,
58 | obj: { foo: 'bar' },
59 | arr: [1, '2'],
60 | regexp: /^test?$/,
61 | date: new Date(),
62 | buffer: new Buffer('data'),
63 | set: new Set([1, 2, 3]),
64 | map: new Map([['a': 1],['b': 2]])
65 | }
66 | console.log(serialize(obj))
67 | //> '{str: "\u003Cscript\u003Evar a = 0 \u003E 1\u003C\u002Fscript\u003E",
68 | //> num: 3.1415, bool: true, nil: null, undef: undefined,
69 | //> obj: {foo: "bar"}, arr: [1, "2"], regexp: new RegExp("^test?$", ""),
70 | //> date: new Date("2019-12-29T10:37:36.613Z"),
71 | //> buffer: Buffer.from("ZGF0YQ==", "base64"), set: new Set([1, 2, 3]),
72 | //> map: new Map([["a", 1], ["b", 2]])}'
73 | ```
74 |
75 | #### Example - serializing while respecting references
76 |
77 | ```js
78 | var serialize = require('serialize-to-js')
79 | var obj = { object: { regexp: /^test?$/ } };
80 | obj.reference = obj.object;
81 | var opts = { reference: true };
82 | console.log(serialize(obj, opts));
83 | //> {object: {regexp: /^test?$/}}
84 | console.log(opts.references);
85 | //> [ [ '.reference', '.object' ] ]
86 | ```
87 |
88 | **Parameters**
89 |
90 | **source**: `Object | Array | function | Any`, source to serialize
91 | **opts**: `Object`, options
92 | **opts.ignoreCircular**: `Boolean`, ignore circular objects
93 | **opts.reference**: `Boolean`, reference instead of a copy (requires post-processing of opts.references)
94 | **opts.unsafe**: `Boolean`, do not escape chars `<>/`
95 | **Returns**: `String`, serialized representation of `source`
96 |
97 |
98 | ## Contribution and License Agreement
99 |
100 | If you contribute code to this project, you are implicitly allowing your
101 | code to be distributed under the MIT license. You are also implicitly
102 | verifying that all code is your original work or correctly attributed
103 | with the source of its origin and licence.
104 |
105 | ## License
106 |
107 | Copyright (c) 2016- commenthol (MIT License)
108 |
109 | See [LICENSE][] for more info.
110 |
111 | [LICENSE]: ./LICENSE
112 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serialize-to-js",
3 | "version": "3.1.2",
4 | "description": "serialize objects to javascript",
5 | "keywords": [
6 | "javascript",
7 | "objects",
8 | "serialize"
9 | ],
10 | "homepage": "https://github.com/commenthol/serialize-to-js",
11 | "bugs": {
12 | "url": "https://github.com/commenthol/serialize-to-js/issues"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/commenthol/serialize-to-js.git"
17 | },
18 | "license": "MIT",
19 | "author": "commenthol ",
20 | "maintainers": "commenthol ",
21 | "main": "lib",
22 | "module": "src",
23 | "directories": {
24 | "lib": "lib",
25 | "test": "test"
26 | },
27 | "files": [
28 | "src",
29 | "lib"
30 | ],
31 | "scripts": {
32 | "build": "babel -d lib src",
33 | "ci": "npm run clean && npm run lint && npm run build && npm test",
34 | "clean": "rimraf lib doc coverage .nyc_output *.tgz",
35 | "coverage": "nyc -r text -r html npm test",
36 | "lint": "eslint src test",
37 | "prepublishOnly": "npm run ci",
38 | "readme": "markedpp --githubid -i README.md -o README.md",
39 | "test": "mocha"
40 | },
41 | "babel": {
42 | "presets": [
43 | "@babel/preset-env"
44 | ]
45 | },
46 | "eslintConfig": {
47 | "env": {
48 | "mocha": true
49 | },
50 | "plugins": [
51 | "standard"
52 | ],
53 | "extends": "standard",
54 | "rules": {
55 | "key-spacing": 0,
56 | "no-console": 1
57 | }
58 | },
59 | "mocha": {
60 | "check-leaks": true
61 | },
62 | "dependencies": {},
63 | "devDependencies": {
64 | "@babel/cli": "^7.17.6",
65 | "@babel/core": "^7.17.8",
66 | "@babel/preset-env": "^7.16.11",
67 | "eslint": "^7.32.0",
68 | "eslint-config-standard": "^14.1.1",
69 | "eslint-plugin-import": "^2.25.4",
70 | "eslint-plugin-node": "^11.1.0",
71 | "eslint-plugin-promise": "^4.3.1",
72 | "eslint-plugin-standard": "^4.1.0",
73 | "mocha": "^9.2.2",
74 | "nyc": "^15.1.0",
75 | "rimraf": "^3.0.2"
76 | },
77 | "engines": {
78 | "node": ">=4.0.0"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @copyright 2016- commenthol
3 | * @license MIT
4 | */
5 |
6 | 'use strict'
7 |
8 | // dependencies
9 | const utils = require('./internal/utils')
10 | const Ref = require('./internal/reference')
11 |
12 | /**
13 | * serializes an object to javascript
14 | *
15 | * @example serializing regex, date, buffer, ...
16 | * const serialize = require('serialize-to-js')
17 | * const obj = {
18 | * str: '',
19 | * num: 3.1415,
20 | * bool: true,
21 | * nil: null,
22 | * undef: undefined,
23 | * obj: { foo: 'bar' },
24 | * arr: [1, '2'],
25 | * regexp: /^test?$/,
26 | * date: new Date(),
27 | * buffer: new Buffer('data'),
28 | * set: new Set([1, 2, 3]),
29 | * map: new Map([['a': 1],['b': 2]])
30 | * }
31 | * console.log(serialize(obj))
32 | * //> '{str: "\u003Cscript\u003Evar a = 0 \u003E 1\u003C\u002Fscript\u003E",
33 | * //> num: 3.1415, bool: true, nil: null, undef: undefined,
34 | * //> obj: {foo: "bar"}, arr: [1, "2"], regexp: new RegExp("^test?$", ""),
35 | * //> date: new Date("2019-12-29T10:37:36.613Z"),
36 | * //> buffer: Buffer.from("ZGF0YQ==", "base64"), set: new Set([1, 2, 3]),
37 | * //> map: new Map([["a", 1], ["b", 2]])}'
38 | *
39 | * @example serializing while respecting references
40 | * const serialize = require('serialize-to-js')
41 | * const obj = { object: { regexp: /^test?$/ } };
42 | * obj.reference = obj.object;
43 | * const opts = { reference: true };
44 | * console.log(serialize(obj, opts));
45 | * //> {object: {regexp: /^test?$/}}
46 | * console.log(opts.references);
47 | * //> [ [ '.reference', '.object' ] ]
48 | *
49 | * @param {Object|Array|Function|Any} source - source to serialize
50 | * @param {Object} [opts] - options
51 | * @param {Boolean} opts.ignoreCircular - ignore circular objects
52 | * @param {Boolean} opts.reference - reference instead of a copy (requires post-processing of opts.references)
53 | * @param {Boolean} opts.unsafe - do not escape chars `<>/`
54 | * @return {String} serialized representation of `source`
55 | */
56 | function serialize (source, opts = {}) {
57 | opts = opts || {}
58 |
59 | const visited = new Set()
60 | opts.references = []
61 | const refs = new Ref(opts.references, opts)
62 |
63 | function stringify (source, opts) {
64 | const type = utils.toType(source)
65 |
66 | if (visited.has(source)) {
67 | if (opts.ignoreCircular) {
68 | switch (type) {
69 | case 'Array':
70 | return '[/*[Circular]*/]'
71 | case 'Object':
72 | return '{/*[Circular]*/}'
73 | default:
74 | return 'undefined /*[Circular]*/'
75 | }
76 | } else {
77 | throw new Error('can not convert circular structures.')
78 | }
79 | }
80 |
81 | switch (type) {
82 | case 'Null':
83 | return 'null'
84 | case 'String':
85 | return utils.quote(source, opts) || '""'
86 | case 'Function': {
87 | const _tmp = source.toString()
88 | const tmp = opts.unsafe ? _tmp : utils.saferFunctionString(_tmp, opts)
89 | // append function to es6 function within obj
90 | return !/^\s*(function|\([^)]*?\)\s*=>)/m.test(tmp) ? 'function ' + tmp : tmp
91 | }
92 | case 'RegExp':
93 | return `new RegExp(${utils.quote(source.source, opts)}, "${source.flags}")`
94 | case 'Date':
95 | if (utils.isInvalidDate(source)) return 'new Date("Invalid Date")'
96 | return `new Date(${utils.quote(source.toJSON(), opts)})`
97 | case 'Error':
98 | return `new Error(${utils.quote(source.message, opts)})`
99 | case 'Buffer':
100 | return `Buffer.from("${source.toString('base64')}", "base64")`
101 | case 'Array': {
102 | visited.add(source)
103 | const tmp = source.map(item => stringify(item, opts))
104 | visited.delete(source)
105 | return `[${tmp.join(', ')}]`
106 | }
107 | case 'Int8Array':
108 | case 'Uint8Array':
109 | case 'Uint8ClampedArray':
110 | case 'Int16Array':
111 | case 'Uint16Array':
112 | case 'Int32Array':
113 | case 'Uint32Array':
114 | case 'Float32Array':
115 | case 'Float64Array': {
116 | const tmp = []
117 | for (let i = 0; i < source.length; i++) {
118 | tmp.push(source[i])
119 | }
120 | return `new ${type}([${tmp.join(', ')}])`
121 | }
122 | case 'Set': {
123 | visited.add(source)
124 | const tmp = Array.from(source).map(item => stringify(item, opts))
125 | visited.delete(source)
126 | return `new ${type}([${tmp.join(', ')}])`
127 | }
128 | case 'Map': {
129 | visited.add(source)
130 | const tmp = Array.from(source).map(([key, value]) => `[${stringify(key, opts)}, ${stringify(value, opts)}]`)
131 | visited.delete(source)
132 | return `new ${type}([${tmp.join(', ')}])`
133 | }
134 | case 'Object': {
135 | visited.add(source)
136 | const tmp = []
137 | for (const key in source) {
138 | if (Object.prototype.hasOwnProperty.call(source, key)) {
139 | if (opts.reference && utils.isObject(source[key])) {
140 | refs.push(key)
141 | if (!refs.hasReference(source[key])) {
142 | tmp.push(Ref.wrapkey(key, opts) + ': ' + stringify(source[key], opts))
143 | }
144 | refs.pop()
145 | } else {
146 | tmp.push(Ref.wrapkey(key, opts) + ': ' + stringify(source[key], opts))
147 | }
148 | }
149 | }
150 | visited.delete(source)
151 | return `{${tmp.join(', ')}}`
152 | }
153 | default:
154 | return '' + source
155 | }
156 | }
157 |
158 | return stringify(source, opts)
159 | }
160 |
161 | module.exports = serialize
162 |
--------------------------------------------------------------------------------
/src/internal/reference.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @copyright 2015- commenthol
3 | * @license MIT
4 | */
5 |
6 | 'use strict'
7 |
8 | const utils = require('./utils')
9 |
10 | const KEY = /^[a-zA-Z$_][a-zA-Z$_0-9]*$/
11 |
12 | /**
13 | * handle references
14 | * @constructor
15 | * @param {Object} references
16 | * @param {boolean} opts.unsafe
17 | */
18 | function Ref (references, opts) {
19 | this.keys = []
20 | this.refs = []
21 | this.key = []
22 | this.references = references || []
23 | this._opts = opts || {}
24 | }
25 |
26 | /**
27 | * wrap an object key
28 | * @api private
29 | * @param {String} key - objects key
30 | * @return {String} wrapped key in quotes if necessary
31 | */
32 | Ref.wrapkey = function (key, opts) {
33 | return (KEY.test(key) ? key : utils.quote(key, opts))
34 | }
35 |
36 | Ref.prototype = {
37 | /**
38 | * push `key` to interal array
39 | * @param {String} key
40 | */
41 | push: function (key) {
42 | this.key.push(key)
43 | },
44 | /**
45 | * remove the last key from internal array
46 | */
47 | pop: function () {
48 | this.key.pop()
49 | },
50 | /**
51 | * join the keys
52 | */
53 | join: function (key) {
54 | let out = ''
55 | key = key || this.key
56 | if (typeof key === 'string') {
57 | key = [key]
58 | }
59 |
60 | key.forEach(k => {
61 | if (KEY.test(k)) {
62 | out += '.' + k
63 | } else {
64 | out += '[' + Ref.wrapkey(k, this._opts) + ']'
65 | }
66 | })
67 | return out
68 | },
69 | /**
70 | * check if object `source` has an already known reference.
71 | * If so then origin and source are stored in `opts.reference`
72 | * @param {Object} source - object to compare
73 | * @return {Boolean}
74 | */
75 | hasReference: function (source) {
76 | let idx
77 | if (~(idx = this.refs.indexOf(source))) {
78 | this.references.push([this.join(), this.keys[idx]])
79 | return true
80 | } else {
81 | this.refs.push(source)
82 | this.keys.push(this.join())
83 | }
84 | },
85 | /**
86 | * get the references array
87 | * @return {Array} references array
88 | */
89 | getReferences: function () {
90 | return this.references
91 | }
92 | }
93 |
94 | module.exports = Ref
95 |
--------------------------------------------------------------------------------
/src/internal/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const UNSAFE_CHARS_REGEXP = /[<>\u2028\u2029/\\\r\n\t"]/g
4 | const CHARS_REGEXP = /[\\\r\n\t"]/g
5 |
6 | const UNICODE_CHARS = {
7 | '"': '\\"',
8 | '\n': '\\n',
9 | '\r': '\\r',
10 | '\t': '\\t',
11 | '\\': '\\u005C',
12 | '<': '\\u003C',
13 | '>': '\\u003E',
14 | '/': '\\u002F',
15 | '\u2028': '\\u2028',
16 | '\u2029': '\\u2029'
17 | }
18 |
19 | function safeString (str) {
20 | return str.replace(UNSAFE_CHARS_REGEXP, (unsafeChar) => {
21 | return UNICODE_CHARS[unsafeChar]
22 | })
23 | }
24 |
25 | function unsafeString (str) {
26 | str = str.replace(CHARS_REGEXP, (unsafeChar) => UNICODE_CHARS[unsafeChar])
27 | return str
28 | }
29 |
30 | function quote (str, opts) {
31 | const fn = opts.unsafe ? unsafeString : safeString
32 | return str ? `"${fn(str)}"` : ''
33 | }
34 |
35 | function saferFunctionString (str, opts) {
36 | return opts.unsafe
37 | ? str
38 | : str.replace(/(<\/?)([a-z][^>]*?>)/ig, (m, m1, m2) => safeString(m1) + m2)
39 | }
40 |
41 | function isObject (arg) {
42 | return typeof arg === 'object' && arg !== null
43 | }
44 |
45 | function isBuffer (arg) {
46 | return arg instanceof Buffer
47 | }
48 |
49 | function isInvalidDate (arg) {
50 | return isNaN(arg.getTime())
51 | }
52 |
53 | function toType (o) {
54 | const _type = Object.prototype.toString.call(o)
55 | const type = _type.substring(8, _type.length - 1)
56 | if (type === 'Uint8Array' && isBuffer(o)) return 'Buffer'
57 | return type
58 | }
59 |
60 | module.exports = {
61 | safeString,
62 | unsafeString,
63 | quote,
64 | saferFunctionString,
65 | isBuffer,
66 | isObject,
67 | isInvalidDate,
68 | toType
69 | }
70 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | /* eslint no-new-func: off */
2 |
3 | 'use strict'
4 |
5 | const assert = require('assert')
6 | const serialize = require('../src')
7 |
8 | if (typeof assert.deepStrictEqual === 'undefined') {
9 | assert.deepStrictEqual = assert.deepEqual // eslint-disable-line
10 | }
11 |
12 | const isBrowser = (typeof window !== 'undefined')
13 |
14 | function log (arg) {
15 | // eslint-disable-next-line no-console
16 | console.log(JSON.stringify(arg))
17 | }
18 |
19 | const isLessV12 = parseInt(process.versions.node.split('.')[0]) < 12
20 |
21 | describe.node = isBrowser ? describe.skip : describe
22 |
23 | describe('serialize-to-js', function () {
24 | function test (name, inp, exp, unsafe) {
25 | it(name, function () {
26 | const res = serialize(inp, { unsafe })
27 | if (typeof exp === 'object') {
28 | assert.deepStrictEqual(res, exp)
29 | } else {
30 | assert.strictEqual(res, exp)
31 | }
32 | })
33 | }
34 |
35 | describe('safe mode', function () {
36 | test('undefined', undefined, 'undefined')
37 | test('null', null, 'null')
38 | test('boolean', true, 'true')
39 | test('number', 3.1415, '3.1415')
40 | test('zero', 0, '0')
41 | test('number int', 3, '3')
42 | test('number negative int', -13, '-13')
43 | test('number float', 0.1, '0.1')
44 | test('number negative float', -0.2, '-0.2')
45 | test('NaN', NaN, 'NaN')
46 | test('Infinity', Infinity, 'Infinity')
47 | test('string', "string's\n\"new\" line", '"string\'s\\n\\"new\\" line"')
48 | test('empty string', '', '""')
49 | test('nul string', '\0', '"\u0000"')
50 | test('string with unsafe characters',
51 | '',
52 | '"\\u003Cscript type=\\"application\\u002Fjavascript\\"\\u003E\\u2028\\u2029\\nvar a = 0;\\nvar b = 1; a \\u003E 1;\\n\\u003C\\u002Fscript\\u003E"'
53 | )
54 | test('string with all unsafe characters', '<>\\\\ \t\n/', '"\\u003C\\u003E\\u005C\\u005C \\t\\n\\u002F"')
55 | test('empty object', {}, '{}')
56 | test('object', { a: 1, b: 2 }, '{a: 1, b: 2}')
57 | test('object with backslash', { backslash: '\\' }, '{backslash: "\\u005C"}')
58 | test('object of primitives',
59 | { one: true, two: false, 'thr-ee': undefined, four: 1, 5: 3.1415, six: -17, 'se ven': 'string' },
60 | '{"5": 3.1415, one: true, two: false, "thr-ee": undefined, four: 1, six: -17, "se ven": "string"}'
61 | )
62 | test('object with unsafe property name',
63 | { "',
150 | '""',
151 | true
152 | )
153 | test('object with unsafe property name',
154 | { "