1import * as Cause from "effect/Cause";
2
3const MAX_ERROR_TRACE_NODES = 128;
4
5export function findErrorTraceId(error: unknown): string | null {
6 const seen = new Set<object>();
7 const pending: Array<unknown> = [error];
8 let inspectedNodeCount = 0;
9
10 while (pending.length > 0 && inspectedNodeCount < MAX_ERROR_TRACE_NODES) {
11 const current = pending.pop();
12 inspectedNodeCount += 1;
13 if (typeof current !== "object" || current === null || seen.has(current)) {
14 continue;
15 }
16 seen.add(current);
17 const record = current as {
18 readonly cause?: unknown;
19 readonly errors?: unknown;
20 readonly traceId?: unknown;
21 };
22 if (typeof record.traceId === "string" && record.traceId.trim().length > 0) {
23 return record.traceId;
24 }
25
26 if (Array.isArray(record.errors)) {
27 for (let index = record.errors.length - 1; index >= 0; index -= 1) {
28 pending.push(record.errors[index]);
29 }
30 }
31 if (Cause.isCause(current)) {
32 for (let index = current.reasons.length - 1; index >= 0; index -= 1) {
33 const reason = current.reasons[index];
34 switch (reason?._tag) {
35 case "Fail":
36 pending.push(reason.error);
37 break;
38 case "Die":
39 pending.push(reason.defect);
40 break;
41 }
42 }
43 }
44 if ("cause" in record) {
45 pending.push(record.cause);
46 }
47 }
48
49 return null;
50}