(err: unknown, contextMessage?: string)
| 25 | * ``` |
| 26 | */ |
| 27 | export function toError(err: unknown, contextMessage?: string): Error { |
| 28 | // If it's already an Error, return it as-is when there's no context to add. |
| 29 | if (err instanceof Error) { |
| 30 | if (!contextMessage) { |
| 31 | return err; |
| 32 | } |
| 33 | // Do NOT mutate the caller's Error. Mutating err.message compounds context |
| 34 | // prefixes when the same Error instance is reported through multiple layers |
| 35 | // (e.g. "outer: inner: original"). Return a fresh Error that prepends the |
| 36 | // context while preserving the original name and stack trace. |
| 37 | const wrapped = new Error(`${contextMessage}: ${err.message}`); |
| 38 | wrapped.name = err.name; |
| 39 | wrapped.stack = err.stack; |
| 40 | return wrapped; |
| 41 | } |
| 42 | |
| 43 | // If it's a string, create a new Error with it |
| 44 | if (typeof err === 'string') { |
| 45 | return new Error(contextMessage ? `${contextMessage}: ${err}` : err); |
| 46 | } |
| 47 | |
| 48 | // For everything else, convert to string and create an Error |
| 49 | const errorMessage = contextMessage |
| 50 | ? `${contextMessage}: ${String(err)}` |
| 51 | : String(err); |
| 52 | |
| 53 | return new Error(errorMessage); |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Checks if an error indicates user cancellation rather than a real error. |
no outgoing calls
no test coverage detected