Skip to content

Commit b39b1c7

Browse files
committed
fix: Dont mutate original input in decycle util function
1 parent b0cabe5 commit b39b1c7

File tree

3 files changed

+98
-5
lines changed

3 files changed

+98
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- [browser] fix: DOMError and DOMException should be error level events
6+
- [utils] fix: Dont mutate original input in decycle util function
67
- [utils] ref: Update wrap method to hide internal sentry flags
78
- [utils] fix: Make internal Sentry flags non-enumerable in fill util
89

packages/utils/src/object.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { SentryWrappedFunction } from '@sentry/types';
2-
import { isNaN, isPlainObject, isPrimitive, isUndefined } from './is';
2+
import { isArray, isNaN, isPlainObject, isPrimitive, isUndefined } from './is';
33
import { Memo } from './memo';
44
import { truncate } from './string';
55

@@ -153,7 +153,7 @@ export function serializeObject<T>(value: T, depth: number): T | string | {} {
153153
});
154154

155155
return serialized;
156-
} else if (Array.isArray(value)) {
156+
} else if (isArray(value)) {
157157
const val = (value as any) as T[];
158158
return val.map(v => serializeObject(v, depth - 1));
159159
}
@@ -325,19 +325,23 @@ function normalizeValue(value: any, key?: any): any {
325325
* @param obj Object to be decycled
326326
* @param memo Optional Memo class handling decycling
327327
*/
328-
function decycle(obj: any, memo: Memo = new Memo()): any {
328+
export function decycle(obj: any, memo: Memo = new Memo()): any {
329+
// tslint:disable-next-line:no-unsafe-any
330+
const copy = isArray(obj) ? [...obj] : isPlainObject(obj) ? { ...obj } : obj;
331+
329332
if (!isPrimitive(obj)) {
330333
if (memo.memoize(obj)) {
331334
return '[Circular ~]';
332335
}
333336
// tslint:disable-next-line
334337
for (const key in obj) {
335338
// tslint:disable-next-line
336-
obj[key] = decycle(obj[key], memo);
339+
copy[key] = decycle(obj[key], memo);
337340
}
338341
memo.unmemoize(obj);
339342
}
340-
return obj;
343+
344+
return copy;
341345
}
342346

343347
/**

packages/utils/test/object.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
clone,
3+
decycle,
34
deserialize,
45
fill,
56
safeNormalize,
@@ -24,6 +25,93 @@ describe('clone()', () => {
2425
}
2526
});
2627

28+
describe('decycle()', () => {
29+
test('decycles circular objects', () => {
30+
const circular = {
31+
foo: 1,
32+
};
33+
circular.bar = circular;
34+
35+
const decycled = decycle(circular);
36+
37+
expect(decycled).toEqual({
38+
foo: 1,
39+
bar: '[Circular ~]',
40+
});
41+
});
42+
43+
test('decycles complex circular objects', () => {
44+
const circular = {
45+
foo: 1,
46+
};
47+
circular.bar = [
48+
{
49+
baz: circular,
50+
},
51+
circular,
52+
];
53+
circular.qux = circular.bar[0].baz;
54+
55+
const decycled = decycle(circular);
56+
57+
expect(decycled).toEqual({
58+
bar: [
59+
{
60+
baz: '[Circular ~]',
61+
},
62+
'[Circular ~]',
63+
],
64+
foo: 1,
65+
qux: '[Circular ~]',
66+
});
67+
});
68+
69+
test('dont mutate original object', () => {
70+
const circular = {
71+
foo: 1,
72+
};
73+
circular.bar = circular;
74+
75+
const decycled = decycle(circular);
76+
77+
expect(decycled).toEqual({
78+
foo: 1,
79+
bar: '[Circular ~]',
80+
});
81+
expect(circular.bar).toEqual(circular);
82+
});
83+
84+
test('dont mutate original complex object', () => {
85+
const circular = {
86+
foo: 1,
87+
};
88+
circular.bar = [
89+
{
90+
baz: circular,
91+
},
92+
circular,
93+
];
94+
circular.qux = circular.bar[0].baz;
95+
96+
const decycled = decycle(circular);
97+
98+
expect(decycled).toEqual({
99+
bar: [
100+
{
101+
baz: '[Circular ~]',
102+
},
103+
'[Circular ~]',
104+
],
105+
foo: 1,
106+
qux: '[Circular ~]',
107+
});
108+
109+
expect(circular.bar[0].baz).toEqual(circular);
110+
expect(circular.bar[1]).toEqual(circular);
111+
expect(circular.qux).toEqual(circular.bar[0].baz);
112+
});
113+
});
114+
27115
describe('serialize()', () => {
28116
for (const entry of MATRIX) {
29117
test(`serializes a ${entry.name}`, () => {

0 commit comments

Comments
 (0)