Skip to content

Commit 75177cd

Browse files
authored
fix(util-waiter): fix for circular refs in waiter debug logs (#1756)
1 parent 6da0ab3 commit 75177cd

File tree

5 files changed

+91
-11
lines changed

5 files changed

+91
-11
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/util-waiter": patch
3+
---
4+
5+
handle circular refs in debug messages
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import { getCircularReplacer } from "./circularReplacer";
4+
5+
describe("getCircularReplacer", () => {
6+
it("should handle nested circular references", () => {
7+
const x = {
8+
a: 1,
9+
b: 2,
10+
c: {
11+
d: {
12+
e: -1,
13+
f: 3,
14+
g: {
15+
h: -1,
16+
},
17+
},
18+
},
19+
} as any;
20+
21+
x.c.d.e = x;
22+
x.c.d.g.h = x;
23+
24+
expect(
25+
JSON.parse(
26+
JSON.stringify(
27+
{
28+
x,
29+
},
30+
getCircularReplacer()
31+
)
32+
)
33+
).toEqual({
34+
x: {
35+
a: 1,
36+
b: 2,
37+
c: {
38+
d: {
39+
e: "[Circular]",
40+
f: 3,
41+
g: {
42+
h: "[Circular]",
43+
},
44+
},
45+
},
46+
},
47+
});
48+
});
49+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Helper for JSON stringification debug logging.
3+
*
4+
* @internal
5+
*/
6+
export const getCircularReplacer = () => {
7+
const seen = new WeakSet();
8+
return (key: any, value: any) => {
9+
if (typeof value === "object" && value !== null) {
10+
if (seen.has(value)) {
11+
return "[Circular]";
12+
}
13+
seen.add(value);
14+
}
15+
return value;
16+
};
17+
};

packages/util-waiter/src/poller.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getCircularReplacer } from "./circularReplacer";
12
import { sleep } from "./utils/sleep";
23
import type { WaiterOptions, WaiterResult } from "./waiter";
34
import { WaiterState } from "./waiter";
@@ -21,7 +22,7 @@ const randomInRange = (min: number, max: number) => min + Math.random() * (max -
2122
* @param params - options passed to the waiter.
2223
* @param client - AWS SDK Client
2324
* @param input - client input
24-
* @param stateChecker - function that checks the acceptor states on each poll.
25+
* @param acceptorChecks - function that checks the acceptor states on each poll.
2526
*/
2627
export const runPolling = async <Client, Input>(
2728
{ minDelay, maxDelay, maxWaitTime, abortController, client, abortSignal }: WaiterOptions<Client>,
@@ -96,5 +97,5 @@ const createMessageFromResponse = (reason: any): string => {
9697
return `${reason.$metadata.httpStatusCode}: OK`;
9798
}
9899
// is an unknown object.
99-
return String(reason?.message ?? JSON.stringify(reason) ?? "Unknown");
100+
return String(reason?.message ?? JSON.stringify(reason, getCircularReplacer()) ?? "Unknown");
100101
};

packages/util-waiter/src/waiter.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { WaiterConfiguration as WaiterConfiguration__ } from "@smithy/types";
22

3+
import { getCircularReplacer } from "./circularReplacer";
4+
35
/**
46
* @internal
57
*/
@@ -57,24 +59,30 @@ export type WaiterResult = {
5759
export const checkExceptions = (result: WaiterResult): WaiterResult => {
5860
if (result.state === WaiterState.ABORTED) {
5961
const abortError = new Error(
60-
`${JSON.stringify({
61-
...result,
62-
reason: "Request was aborted",
63-
})}`
62+
`${JSON.stringify(
63+
{
64+
...result,
65+
reason: "Request was aborted",
66+
},
67+
getCircularReplacer()
68+
)}`
6469
);
6570
abortError.name = "AbortError";
6671
throw abortError;
6772
} else if (result.state === WaiterState.TIMEOUT) {
6873
const timeoutError = new Error(
69-
`${JSON.stringify({
70-
...result,
71-
reason: "Waiter has timed out",
72-
})}`
74+
`${JSON.stringify(
75+
{
76+
...result,
77+
reason: "Waiter has timed out",
78+
},
79+
getCircularReplacer()
80+
)}`
7381
);
7482
timeoutError.name = "TimeoutError";
7583
throw timeoutError;
7684
} else if (result.state !== WaiterState.SUCCESS) {
77-
throw new Error(`${JSON.stringify(result)}`);
85+
throw new Error(`${JSON.stringify(result, getCircularReplacer())}`);
7886
}
7987
return result;
8088
};

0 commit comments

Comments
 (0)