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