Skip to content

Commit 4979fa5

Browse files
authored
Merge pull request #883 from getsentry/vendor-stringify-safe
Vendor+IE8-ify json-stringify-safe, fix handler with XDomainRequest
2 parents 9805b06 + d911cb7 commit 4979fa5

File tree

5 files changed

+322
-15
lines changed

5 files changed

+322
-15
lines changed

example/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
<button onclick="testSynthetic()">test synthetic</button>
3737
<button onclick="throwString()">throw string</button>
3838
<button onclick="showDialog()">show dialog</button>
39+
<button onclick="captureMessage()">capture message</button>
40+
<button onclick="captureException()">capture exception</button>
3941
<button onclick="blobExample()">blob example</button>
4042
<input/>
4143

example/scratch.js

+8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ function blobExample() {
6868
xhr.send();
6969
}
7070

71+
function captureMessage() {
72+
Raven.captureMessage('lol did it broke');
73+
}
74+
75+
function captureException() {
76+
Raven.captureException(new Error('lol did it broke'));
77+
}
78+
7179
function a() { b(); }
7280
function b() { c(); }
7381
function c() { d(); }

src/raven.js

+17-15
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
'use strict';
33

44
var TraceKit = require('../vendor/TraceKit/tracekit');
5+
var stringify = require('../vendor/json-stringify-safe/stringify');
56
var RavenConfigError = require('./configError');
67
var utils = require('./utils');
78

89
var isError = utils.isError,
910
isObject = utils.isObject;
1011

11-
var stringify = require('json-stringify-safe');
12-
1312
var wrapConsoleMethod = require('./console').wrapMethod;
1413

1514
var dsnKeys = 'source protocol user pass host port path'.split(' '),
@@ -1592,24 +1591,18 @@ Raven.prototype = {
15921591
if (!hasCORS) return;
15931592

15941593
var url = opts.url;
1595-
function handler() {
1596-
if (request.status === 200) {
1597-
if (opts.onSuccess) {
1598-
opts.onSuccess();
1599-
}
1600-
} else if (opts.onError) {
1601-
var err = new Error('Sentry error code: ' + request.status);
1602-
err.request = request;
1603-
opts.onError(err);
1604-
}
1605-
}
16061594

16071595
if ('withCredentials' in request) {
16081596
request.onreadystatechange = function () {
16091597
if (request.readyState !== 4) {
16101598
return;
1599+
} else if (request.status === 200) {
1600+
opts.onSuccess && opts.onSuccess();
1601+
} else if (opts.onError) {
1602+
var err = new Error('Sentry error code: ' + request.status);
1603+
err.request = request;
1604+
opts.onError(err);
16111605
}
1612-
handler();
16131606
};
16141607
} else {
16151608
request = new XDomainRequest();
@@ -1618,7 +1611,16 @@ Raven.prototype = {
16181611
url = url.replace(/^https?:/, '');
16191612

16201613
// onreadystatechange not supported by XDomainRequest
1621-
request.onload = handler;
1614+
if (opts.onSuccess) {
1615+
request.onload = opts.onSuccess;
1616+
}
1617+
if (opts.onError) {
1618+
request.onerror = function () {
1619+
var err = new Error('Sentry error code: XDomainRequest');
1620+
err.request = request;
1621+
opts.onError(err);
1622+
}
1623+
}
16221624
}
16231625

16241626
// NOTE: auth is intentionally sent as part of query string (NOT as custom
+248
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/*global Mocha, assert*/
2+
var Sinon = require("sinon")
3+
var stringify = require('../../vendor/json-stringify-safe/stringify');
4+
5+
function jsonify(obj) { return JSON.stringify(obj, null, 2) }
6+
7+
describe("Stringify", function() {
8+
it("must stringify circular objects", function() {
9+
var obj = {name: "Alice"}
10+
obj.self = obj
11+
var json = stringify(obj, null, 2)
12+
assert.deepEqual(json, jsonify({name: "Alice", self: "[Circular ~]"}))
13+
})
14+
15+
it("must stringify circular objects with intermediaries", function() {
16+
var obj = {name: "Alice"}
17+
obj.identity = {self: obj}
18+
var json = stringify(obj, null, 2)
19+
assert.deepEqual(json, jsonify({name: "Alice", identity: {self: "[Circular ~]"}}))
20+
})
21+
22+
it("must stringify circular objects deeper", function() {
23+
var obj = {name: "Alice", child: {name: "Bob"}}
24+
obj.child.self = obj.child
25+
26+
assert.deepEqual(stringify(obj, null, 2), jsonify({
27+
name: "Alice",
28+
child: {name: "Bob", self: "[Circular ~.child]"}
29+
}))
30+
})
31+
32+
it("must stringify circular objects deeper with intermediaries", function() {
33+
var obj = {name: "Alice", child: {name: "Bob"}}
34+
obj.child.identity = {self: obj.child}
35+
36+
assert.deepEqual(stringify(obj, null, 2), jsonify({
37+
name: "Alice",
38+
child: {name: "Bob", identity: {self: "[Circular ~.child]"}}
39+
}))
40+
})
41+
42+
it("must stringify circular objects in an array", function() {
43+
var obj = {name: "Alice"}
44+
obj.self = [obj, obj]
45+
46+
assert.deepEqual(stringify(obj, null, 2), jsonify({
47+
name: "Alice", self: ["[Circular ~]", "[Circular ~]"]
48+
}))
49+
})
50+
51+
it("must stringify circular objects deeper in an array", function() {
52+
var obj = {name: "Alice", children: [{name: "Bob"}, {name: "Eve"}]}
53+
obj.children[0].self = obj.children[0]
54+
obj.children[1].self = obj.children[1]
55+
56+
assert.deepEqual(stringify(obj, null, 2), jsonify({
57+
name: "Alice",
58+
children: [
59+
{name: "Bob", self: "[Circular ~.children.0]"},
60+
{name: "Eve", self: "[Circular ~.children.1]"}
61+
]
62+
}))
63+
})
64+
65+
it("must stringify circular arrays", function() {
66+
var obj = []
67+
obj.push(obj)
68+
obj.push(obj)
69+
var json = stringify(obj, null, 2)
70+
assert.deepEqual(json, jsonify(["[Circular ~]", "[Circular ~]"]))
71+
})
72+
73+
it("must stringify circular arrays with intermediaries", function() {
74+
var obj = []
75+
obj.push({name: "Alice", self: obj})
76+
obj.push({name: "Bob", self: obj})
77+
78+
assert.deepEqual(stringify(obj, null, 2), jsonify([
79+
{name: "Alice", self: "[Circular ~]"},
80+
{name: "Bob", self: "[Circular ~]"}
81+
]))
82+
})
83+
84+
it("must stringify repeated objects in objects", function() {
85+
var obj = {}
86+
var alice = {name: "Alice"}
87+
obj.alice1 = alice
88+
obj.alice2 = alice
89+
90+
assert.deepEqual(stringify(obj, null, 2), jsonify({
91+
alice1: {name: "Alice"},
92+
alice2: {name: "Alice"}
93+
}))
94+
})
95+
96+
it("must stringify repeated objects in arrays", function() {
97+
var alice = {name: "Alice"}
98+
var obj = [alice, alice]
99+
var json = stringify(obj, null, 2)
100+
assert.deepEqual(json, jsonify([{name: "Alice"}, {name: "Alice"}]))
101+
})
102+
103+
it("must call given decycler and use its output", function() {
104+
var obj = {}
105+
obj.a = obj
106+
obj.b = obj
107+
108+
var decycle = Sinon.spy(function() { return decycle.callCount })
109+
var json = stringify(obj, null, 2, decycle)
110+
assert.deepEqual(json, jsonify({a: 1, b: 2}, null, 2))
111+
112+
assert.strictEqual(decycle.callCount, 2)
113+
assert.strictEqual(decycle.thisValues[0], obj)
114+
assert.strictEqual(decycle.args[0][0], "a")
115+
assert.strictEqual(decycle.args[0][1], obj)
116+
assert.strictEqual(decycle.thisValues[1], obj)
117+
assert.strictEqual(decycle.args[1][0], "b")
118+
assert.strictEqual(decycle.args[1][1], obj)
119+
})
120+
121+
it("must call replacer and use its output", function() {
122+
var obj = {name: "Alice", child: {name: "Bob"}}
123+
124+
var replacer = Sinon.spy(bangString)
125+
var json = stringify(obj, replacer, 2)
126+
assert.deepEqual(json, jsonify({name: "Alice!", child: {name: "Bob!"}}))
127+
128+
assert.strictEqual(replacer.callCount, 4)
129+
assert.strictEqual(replacer.args[0][0], "")
130+
assert.strictEqual(replacer.args[0][1], obj)
131+
assert.strictEqual(replacer.thisValues[1], obj)
132+
assert.strictEqual(replacer.args[1][0], "name")
133+
assert.strictEqual(replacer.args[1][1], "Alice")
134+
assert.strictEqual(replacer.thisValues[2], obj)
135+
assert.strictEqual(replacer.args[2][0], "child")
136+
assert.strictEqual(replacer.args[2][1], obj.child)
137+
assert.strictEqual(replacer.thisValues[3], obj.child)
138+
assert.strictEqual(replacer.args[3][0], "name")
139+
assert.strictEqual(replacer.args[3][1], "Bob")
140+
})
141+
142+
it("must call replacer after describing circular references", function() {
143+
var obj = {name: "Alice"}
144+
obj.self = obj
145+
146+
var replacer = Sinon.spy(bangString)
147+
var json = stringify(obj, replacer, 2)
148+
assert.deepEqual(json, jsonify({name: "Alice!", self: "[Circular ~]!"}))
149+
150+
assert.strictEqual(replacer.callCount, 3)
151+
assert.strictEqual(replacer.args[0][0], "")
152+
assert.strictEqual(replacer.args[0][1], obj)
153+
assert.strictEqual(replacer.thisValues[1], obj)
154+
assert.strictEqual(replacer.args[1][0], "name")
155+
assert.strictEqual(replacer.args[1][1], "Alice")
156+
assert.strictEqual(replacer.thisValues[2], obj)
157+
assert.strictEqual(replacer.args[2][0], "self")
158+
assert.strictEqual(replacer.args[2][1], "[Circular ~]")
159+
})
160+
161+
it("must call given decycler and use its output for nested objects",
162+
function() {
163+
var obj = {}
164+
obj.a = obj
165+
obj.b = {self: obj}
166+
167+
var decycle = Sinon.spy(function() { return decycle.callCount })
168+
var json = stringify(obj, null, 2, decycle)
169+
assert.deepEqual(json, jsonify({a: 1, b: {self: 2}}))
170+
171+
assert.strictEqual(decycle.callCount, 2)
172+
assert.strictEqual(decycle.args[0][0], "a")
173+
assert.strictEqual(decycle.args[0][1], obj)
174+
assert.strictEqual(decycle.args[1][0], "self")
175+
assert.strictEqual(decycle.args[1][1], obj)
176+
})
177+
178+
it("must use decycler's output when it returned null", function() {
179+
var obj = {a: "b"}
180+
obj.self = obj
181+
obj.selves = [obj, obj]
182+
183+
function decycle() { return null }
184+
assert.deepEqual(stringify(obj, null, 2, decycle), jsonify({
185+
a: "b",
186+
self: null,
187+
selves: [null, null]
188+
}))
189+
})
190+
191+
it("must use decycler's output when it returned undefined", function() {
192+
var obj = {a: "b"}
193+
obj.self = obj
194+
obj.selves = [obj, obj]
195+
196+
function decycle() {}
197+
assert.deepEqual(stringify(obj, null, 2, decycle), jsonify({
198+
a: "b",
199+
selves: [null, null]
200+
}))
201+
})
202+
203+
it("must throw given a decycler that returns a cycle", function() {
204+
var obj = {}
205+
obj.self = obj
206+
var err
207+
function identity(key, value) { return value }
208+
try { stringify(obj, null, 2, identity) } catch (ex) { err = ex }
209+
assert.ok(err instanceof TypeError)
210+
})
211+
212+
describe(".getSerialize", function() {
213+
it("must stringify circular objects", function() {
214+
var obj = {a: "b"}
215+
obj.circularRef = obj
216+
obj.list = [obj, obj]
217+
218+
var json = JSON.stringify(obj, stringify.getSerialize(), 2)
219+
assert.deepEqual(json, jsonify({
220+
"a": "b",
221+
"circularRef": "[Circular ~]",
222+
"list": ["[Circular ~]", "[Circular ~]"]
223+
}))
224+
})
225+
226+
// This is the behavior as of Mar 3, 2015.
227+
// The serializer function keeps state inside the returned function and
228+
// so far I'm not sure how to not do that. JSON.stringify's replacer is not
229+
// called _after_ serialization.
230+
xit("must return a function that could be called twice", function() {
231+
var obj = {name: "Alice"}
232+
obj.self = obj
233+
234+
var json
235+
var serializer = stringify.getSerialize()
236+
237+
json = JSON.stringify(obj, serializer, 2)
238+
assert.deepEqual(json, jsonify({name: "Alice", self: "[Circular ~]"}))
239+
240+
json = JSON.stringify(obj, serializer, 2)
241+
assert.deepEqual(json, jsonify({name: "Alice", self: "[Circular ~]"}))
242+
})
243+
})
244+
})
245+
246+
function bangString(key, value) {
247+
return typeof value == "string" ? value + "!" : value
248+
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
/*
4+
json-stringify-safe
5+
Like JSON.stringify, but doesn't throw on circular references.
6+
7+
Originally forked from https://github.com/isaacs/json-stringify-safe
8+
version 5.0.1 on 3/8/2017 and modified for IE8 compatibility.
9+
Tests for this are in test/vendor.
10+
11+
ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE
12+
*/
13+
14+
exports = module.exports = stringify
15+
exports.getSerialize = serializer
16+
17+
function indexOf(haystack, needle) {
18+
for (var i = 0; i < haystack.length; ++i) {
19+
if (haystack[i] === needle) return i;
20+
}
21+
return -1;
22+
}
23+
24+
function stringify(obj, replacer, spaces, cycleReplacer) {
25+
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces)
26+
}
27+
28+
function serializer(replacer, cycleReplacer) {
29+
var stack = [], keys = []
30+
31+
if (cycleReplacer == null) cycleReplacer = function(key, value) {
32+
if (stack[0] === value) return '[Circular ~]'
33+
return '[Circular ~.' + keys.slice(0, indexOf(stack, value)).join('.') + ']'
34+
}
35+
36+
return function(key, value) {
37+
if (stack.length > 0) {
38+
var thisPos = indexOf(stack, this);
39+
~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
40+
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
41+
if (~indexOf(stack, value)) value = cycleReplacer.call(this, key, value)
42+
}
43+
else stack.push(value)
44+
45+
return replacer == null ? value : replacer.call(this, key, value)
46+
}
47+
}

0 commit comments

Comments
 (0)