open source is social v-it.org
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 sol pbc
3
4const MAX_CAUSES = 10;
5
6function isObjectLike(value) {
7 return (typeof value === 'object' && value !== null) || typeof value === 'function';
8}
9
10export function errorMessage(value) {
11 if (value instanceof Error) return value.message || String(value);
12 if (isObjectLike(value) && typeof value.message === 'string') return value.message;
13 return String(value);
14}
15
16function stackLinesOf(value) {
17 if (!(value instanceof Error) || typeof value.stack !== 'string' || value.stack === '') return [];
18 return value.stack.split('\n').map(line => ` ${line}`);
19}
20
21function walkChain(err) {
22 if (!(err instanceof Error)) {
23 return [{ value: err, message: String(err), stackLines: [] }];
24 }
25
26 const levels = [];
27 const seen = new Set();
28 let current = err;
29 let causeCount = 0;
30
31 while (current !== undefined) {
32 if (isObjectLike(current)) {
33 if (seen.has(current)) break;
34 seen.add(current);
35 }
36
37 levels.push({
38 value: current,
39 message: errorMessage(current),
40 stackLines: stackLinesOf(current),
41 });
42
43 if (!isObjectLike(current) || !('cause' in current)) break;
44
45 const next = current.cause;
46 if (next === undefined) break;
47 if (causeCount >= MAX_CAUSES) break;
48
49 current = next;
50 causeCount += 1;
51 }
52
53 return levels;
54}
55
56export function collectCauses(err) {
57 if (!(err instanceof Error)) return [];
58 return walkChain(err).slice(1).map(level => level.message);
59}
60
61export function formatError(err, { hint, verbose = false } = {}) {
62 const levels = walkChain(err);
63 const lines = [];
64
65 for (let i = 0; i < levels.length; i += 1) {
66 const level = levels[i];
67 lines.push(i === 0 ? level.message : ` caused by: ${level.message}`);
68 if (verbose && level.stackLines.length > 0) {
69 lines.push(...level.stackLines);
70 }
71 }
72
73 if (typeof hint === 'string' && hint) {
74 lines.push(`hint: ${hint}`);
75 }
76
77 return lines.join('\n');
78}