Skip to content

Commit 0cbbf47

Browse files
committed
fix(logger): no longer detects duplicate object as circular
1 parent bcac255 commit 0cbbf47

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

spec/logger.spec.ts

+44
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,50 @@ describe("logger", () => {
118118
});
119119
});
120120

121+
it("should not detect duplicate object as circular", () => {
122+
const obj: any = { a: "foo" };
123+
const entry: logger.LogEntry = {
124+
severity: "ERROR",
125+
message: "testing circular",
126+
a: obj,
127+
b: obj,
128+
};
129+
logger.write(entry);
130+
expectStderr({
131+
severity: "ERROR",
132+
message: "testing circular",
133+
a: { a: "foo" },
134+
b: { a: "foo" },
135+
});
136+
});
137+
138+
it("should not detect duplicate object in array as circular", () => {
139+
const obj: any = { a: "foo" };
140+
const arr: any = [
141+
{ a: obj, b: obj },
142+
{ a: obj, b: obj },
143+
];
144+
const entry: logger.LogEntry = {
145+
severity: "ERROR",
146+
message: "testing circular",
147+
a: arr,
148+
b: arr,
149+
};
150+
logger.write(entry);
151+
expectStderr({
152+
severity: "ERROR",
153+
message: "testing circular",
154+
a: [
155+
{ a: { a: "foo" }, b: { a: "foo" } },
156+
{ a: { a: "foo" }, b: { a: "foo" } },
157+
],
158+
b: [
159+
{ a: { a: "foo" }, b: { a: "foo" } },
160+
{ a: { a: "foo" }, b: { a: "foo" } },
161+
],
162+
});
163+
});
164+
121165
it("should not break on objects that override toJSON", () => {
122166
const obj: any = { a: new Date("August 26, 1994 12:24:00Z") };
123167

src/logger/index.ts

+13-16
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,25 @@ function removeCircular(obj: any, refs: any[] = []): any {
5656
if (typeof obj !== "object" || !obj) {
5757
return obj;
5858
}
59-
// If the object defines its own toJSON, prefer that.
60-
if (obj.toJSON) {
59+
// If the object defines its own toJSON method, use it.
60+
if (obj.toJSON && typeof obj.toJSON === "function") {
6161
return obj.toJSON();
6262
}
63-
if (refs.includes(obj)) {
63+
// Only check for circularity among ancestors in the recursion stack.
64+
if (refs.indexOf(obj) !== -1) {
6465
return "[Circular]";
65-
} else {
66-
refs.push(obj);
6766
}
68-
let returnObj: any;
69-
if (Array.isArray(obj)) {
70-
returnObj = new Array(obj.length);
71-
} else {
72-
returnObj = {};
73-
}
74-
for (const k in obj) {
75-
if (refs.includes(obj[k])) {
76-
returnObj[k] = "[Circular]";
77-
} else {
78-
returnObj[k] = removeCircular(obj[k], refs);
67+
// Add the current object to the recursion stack.
68+
refs.push(obj);
69+
70+
let returnObj: any = Array.isArray(obj) ? [] : {};
71+
for (const key in obj) {
72+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
73+
returnObj[key] = removeCircular(obj[key], refs);
7974
}
8075
}
76+
// Remove the current object from the stack once its properties are processed.
77+
refs.pop();
8178
return returnObj;
8279
}
8380

0 commit comments

Comments
 (0)