pax_global_header00006660000000000000000000000064125265123500014513gustar00rootroot0000000000000052 comment=3890dceab3ad14f8701e38ca74f38276abc76de5 json-stringify-safe-5.0.1/000077500000000000000000000000001252651235000154175ustar00rootroot00000000000000json-stringify-safe-5.0.1/.gitignore000066400000000000000000000000261252651235000174050ustar00rootroot00000000000000/node_modules/ /*.tgz json-stringify-safe-5.0.1/.npmignore000066400000000000000000000000071252651235000174130ustar00rootroot00000000000000/*.tgz json-stringify-safe-5.0.1/CHANGELOG.md000066400000000000000000000013361252651235000172330ustar00rootroot00000000000000## Unreleased - Fixes stringify to only take ancestors into account when checking circularity. It previously assumed every visited object was circular which led to [false positives][issue9]. Uses the tiny serializer I wrote for [Must.js][must] a year and a half ago. - Fixes calling the `replacer` function in the proper context (`thisArg`). - Fixes calling the `cycleReplacer` function in the proper context (`thisArg`). - Speeds serializing by a factor of Big-O(h-my-god-it-linearly-searched-every-object) it had ever seen. Searching only the ancestors for a circular references speeds up things considerably. [must]: https://github.com/moll/js-must [issue9]: https://github.com/isaacs/json-stringify-safe/issues/9 json-stringify-safe-5.0.1/LICENSE000066400000000000000000000013751252651235000164320ustar00rootroot00000000000000The ISC License Copyright (c) Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. json-stringify-safe-5.0.1/Makefile000066400000000000000000000012431252651235000170570ustar00rootroot00000000000000NODE_OPTS = TEST_OPTS = love: @echo "Feel like makin' love." test: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot $(TEST_OPTS) spec: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec $(TEST_OPTS) autotest: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot --watch $(TEST_OPTS) autospec: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec --watch $(TEST_OPTS) pack: @file=$$(npm pack); echo "$$file"; tar tf "$$file" publish: npm publish tag: git tag "v$$(node -e 'console.log(require("./package").version)')" clean: rm -f *.tgz npm prune --production .PHONY: love .PHONY: test spec autotest autospec .PHONY: pack publish tag .PHONY: clean json-stringify-safe-5.0.1/README.md000066400000000000000000000023551252651235000167030ustar00rootroot00000000000000# json-stringify-safe Like JSON.stringify, but doesn't throw on circular references. ## Usage Takes the same arguments as `JSON.stringify`. ```javascript var stringify = require('json-stringify-safe'); var circularObj = {}; circularObj.circularRef = circularObj; circularObj.list = [ circularObj, circularObj ]; console.log(stringify(circularObj, null, 2)); ``` Output: ```json { "circularRef": "[Circular]", "list": [ "[Circular]", "[Circular]" ] } ``` ## Details ``` stringify(obj, serializer, indent, decycler) ``` The first three arguments are the same as to JSON.stringify. The last is an argument that's only used when the object has been seen already. The default `decycler` function returns the string `'[Circular]'`. If, for example, you pass in `function(k,v){}` (return nothing) then it will prune cycles. If you pass in `function(k,v){ return {foo: 'bar'}}`, then cyclical objects will always be represented as `{"foo":"bar"}` in the result. ``` stringify.getSerialize(serializer, decycler) ``` Returns a serializer that can be used elsewhere. This is the actual function that's passed to JSON.stringify. **Note** that the function returned from `getSerialize` is stateful for now, so do **not** use it more than once. json-stringify-safe-5.0.1/package.json000066400000000000000000000014341252651235000177070ustar00rootroot00000000000000{ "name": "json-stringify-safe", "version": "5.0.1", "description": "Like JSON.stringify, but doesn't blow up on circular refs.", "keywords": [ "json", "stringify", "circular", "safe" ], "homepage": "https://github.com/isaacs/json-stringify-safe", "bugs": "https://github.com/isaacs/json-stringify-safe/issues", "author": "Isaac Z. Schlueter (http://blog.izs.me)", "contributors": [ "Andri Möll (http://themoll.com)" ], "license": "ISC", "repository": { "type": "git", "url": "git://github.com/isaacs/json-stringify-safe" }, "main": "stringify.js", "scripts": { "test": "node test.js" }, "devDependencies": { "mocha": ">= 2.1.0 < 3", "must": ">= 0.12 < 0.13", "sinon": ">= 1.12.2 < 2" } } json-stringify-safe-5.0.1/stringify.js000066400000000000000000000016131252651235000177740ustar00rootroot00000000000000exports = module.exports = stringify exports.getSerialize = serializer function stringify(obj, replacer, spaces, cycleReplacer) { return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) } function serializer(replacer, cycleReplacer) { var stack = [], keys = [] if (cycleReplacer == null) cycleReplacer = function(key, value) { if (stack[0] === value) return "[Circular ~]" return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" } return function(key, value) { if (stack.length > 0) { var thisPos = stack.indexOf(this) ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) } else stack.push(value) return replacer == null ? value : replacer.call(this, key, value) } } json-stringify-safe-5.0.1/test/000077500000000000000000000000001252651235000163765ustar00rootroot00000000000000json-stringify-safe-5.0.1/test/mocha.opts000066400000000000000000000000331252651235000203700ustar00rootroot00000000000000--recursive --require must json-stringify-safe-5.0.1/test/stringify_test.js000066400000000000000000000165761252651235000220300ustar00rootroot00000000000000var Sinon = require("sinon") var stringify = require("..") function jsonify(obj) { return JSON.stringify(obj, null, 2) } describe("Stringify", function() { it("must stringify circular objects", function() { var obj = {name: "Alice"} obj.self = obj var json = stringify(obj, null, 2) json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) }) it("must stringify circular objects with intermediaries", function() { var obj = {name: "Alice"} obj.identity = {self: obj} var json = stringify(obj, null, 2) json.must.eql(jsonify({name: "Alice", identity: {self: "[Circular ~]"}})) }) it("must stringify circular objects deeper", function() { var obj = {name: "Alice", child: {name: "Bob"}} obj.child.self = obj.child stringify(obj, null, 2).must.eql(jsonify({ name: "Alice", child: {name: "Bob", self: "[Circular ~.child]"} })) }) it("must stringify circular objects deeper with intermediaries", function() { var obj = {name: "Alice", child: {name: "Bob"}} obj.child.identity = {self: obj.child} stringify(obj, null, 2).must.eql(jsonify({ name: "Alice", child: {name: "Bob", identity: {self: "[Circular ~.child]"}} })) }) it("must stringify circular objects in an array", function() { var obj = {name: "Alice"} obj.self = [obj, obj] stringify(obj, null, 2).must.eql(jsonify({ name: "Alice", self: ["[Circular ~]", "[Circular ~]"] })) }) it("must stringify circular objects deeper in an array", function() { var obj = {name: "Alice", children: [{name: "Bob"}, {name: "Eve"}]} obj.children[0].self = obj.children[0] obj.children[1].self = obj.children[1] stringify(obj, null, 2).must.eql(jsonify({ name: "Alice", children: [ {name: "Bob", self: "[Circular ~.children.0]"}, {name: "Eve", self: "[Circular ~.children.1]"} ] })) }) it("must stringify circular arrays", function() { var obj = [] obj.push(obj) obj.push(obj) var json = stringify(obj, null, 2) json.must.eql(jsonify(["[Circular ~]", "[Circular ~]"])) }) it("must stringify circular arrays with intermediaries", function() { var obj = [] obj.push({name: "Alice", self: obj}) obj.push({name: "Bob", self: obj}) stringify(obj, null, 2).must.eql(jsonify([ {name: "Alice", self: "[Circular ~]"}, {name: "Bob", self: "[Circular ~]"} ])) }) it("must stringify repeated objects in objects", function() { var obj = {} var alice = {name: "Alice"} obj.alice1 = alice obj.alice2 = alice stringify(obj, null, 2).must.eql(jsonify({ alice1: {name: "Alice"}, alice2: {name: "Alice"} })) }) it("must stringify repeated objects in arrays", function() { var alice = {name: "Alice"} var obj = [alice, alice] var json = stringify(obj, null, 2) json.must.eql(jsonify([{name: "Alice"}, {name: "Alice"}])) }) it("must call given decycler and use its output", function() { var obj = {} obj.a = obj obj.b = obj var decycle = Sinon.spy(function() { return decycle.callCount }) var json = stringify(obj, null, 2, decycle) json.must.eql(jsonify({a: 1, b: 2}, null, 2)) decycle.callCount.must.equal(2) decycle.thisValues[0].must.equal(obj) decycle.args[0][0].must.equal("a") decycle.args[0][1].must.equal(obj) decycle.thisValues[1].must.equal(obj) decycle.args[1][0].must.equal("b") decycle.args[1][1].must.equal(obj) }) it("must call replacer and use its output", function() { var obj = {name: "Alice", child: {name: "Bob"}} var replacer = Sinon.spy(bangString) var json = stringify(obj, replacer, 2) json.must.eql(jsonify({name: "Alice!", child: {name: "Bob!"}})) replacer.callCount.must.equal(4) replacer.args[0][0].must.equal("") replacer.args[0][1].must.equal(obj) replacer.thisValues[1].must.equal(obj) replacer.args[1][0].must.equal("name") replacer.args[1][1].must.equal("Alice") replacer.thisValues[2].must.equal(obj) replacer.args[2][0].must.equal("child") replacer.args[2][1].must.equal(obj.child) replacer.thisValues[3].must.equal(obj.child) replacer.args[3][0].must.equal("name") replacer.args[3][1].must.equal("Bob") }) it("must call replacer after describing circular references", function() { var obj = {name: "Alice"} obj.self = obj var replacer = Sinon.spy(bangString) var json = stringify(obj, replacer, 2) json.must.eql(jsonify({name: "Alice!", self: "[Circular ~]!"})) replacer.callCount.must.equal(3) replacer.args[0][0].must.equal("") replacer.args[0][1].must.equal(obj) replacer.thisValues[1].must.equal(obj) replacer.args[1][0].must.equal("name") replacer.args[1][1].must.equal("Alice") replacer.thisValues[2].must.equal(obj) replacer.args[2][0].must.equal("self") replacer.args[2][1].must.equal("[Circular ~]") }) it("must call given decycler and use its output for nested objects", function() { var obj = {} obj.a = obj obj.b = {self: obj} var decycle = Sinon.spy(function() { return decycle.callCount }) var json = stringify(obj, null, 2, decycle) json.must.eql(jsonify({a: 1, b: {self: 2}})) decycle.callCount.must.equal(2) decycle.args[0][0].must.equal("a") decycle.args[0][1].must.equal(obj) decycle.args[1][0].must.equal("self") decycle.args[1][1].must.equal(obj) }) it("must use decycler's output when it returned null", function() { var obj = {a: "b"} obj.self = obj obj.selves = [obj, obj] function decycle() { return null } stringify(obj, null, 2, decycle).must.eql(jsonify({ a: "b", self: null, selves: [null, null] })) }) it("must use decycler's output when it returned undefined", function() { var obj = {a: "b"} obj.self = obj obj.selves = [obj, obj] function decycle() {} stringify(obj, null, 2, decycle).must.eql(jsonify({ a: "b", selves: [null, null] })) }) it("must throw given a decycler that returns a cycle", function() { var obj = {} obj.self = obj var err function identity(key, value) { return value } try { stringify(obj, null, 2, identity) } catch (ex) { err = ex } err.must.be.an.instanceof(TypeError) }) describe(".getSerialize", function() { it("must stringify circular objects", function() { var obj = {a: "b"} obj.circularRef = obj obj.list = [obj, obj] var json = JSON.stringify(obj, stringify.getSerialize(), 2) json.must.eql(jsonify({ "a": "b", "circularRef": "[Circular ~]", "list": ["[Circular ~]", "[Circular ~]"] })) }) // This is the behavior as of Mar 3, 2015. // The serializer function keeps state inside the returned function and // so far I'm not sure how to not do that. JSON.stringify's replacer is not // called _after_ serialization. xit("must return a function that could be called twice", function() { var obj = {name: "Alice"} obj.self = obj var json var serializer = stringify.getSerialize() json = JSON.stringify(obj, serializer, 2) json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) json = JSON.stringify(obj, serializer, 2) json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) }) }) }) function bangString(key, value) { return typeof value == "string" ? value + "!" : value }