| 1 | import * as Cause from "effect/Cause"; |
| 2 | |
| 3 | const MAX_ERROR_TRACE_NODES = 128; |
| 4 | |
| 5 | export 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 | } |