15815213711
2024-08-26 67b8b6731811983447e053d4396b3708c14dfe3c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
'use strict';
 
class NonError extends Error {
    constructor(message) {
        super(NonError._prepareSuperMessage(message));
        Object.defineProperty(this, 'name', {
            value: 'NonError',
            configurable: true,
            writable: true
        });
 
        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, NonError);
        }
    }
 
    static _prepareSuperMessage(message) {
        try {
            return JSON.stringify(message);
        } catch (_) {
            return String(message);
        }
    }
}
 
const commonProperties = [
    {property: 'name', enumerable: false},
    {property: 'message', enumerable: false},
    {property: 'stack', enumerable: false},
    {property: 'code', enumerable: true}
];
 
const destroyCircular = ({from, seen, to_, forceEnumerable}) => {
    const to = to_ || (Array.isArray(from) ? [] : {});
 
    seen.push(from);
 
    for (const [key, value] of Object.entries(from)) {
        if (typeof value === 'function') {
            continue;
        }
 
        if (!value || typeof value !== 'object') {
            to[key] = value;
            continue;
        }
 
        if (!seen.includes(from[key])) {
            to[key] = destroyCircular({from: from[key], seen: seen.slice(), forceEnumerable});
            continue;
        }
 
        to[key] = '[Circular]';
    }
 
    for (const {property, enumerable} of commonProperties) {
        if (typeof from[property] === 'string') {
            Object.defineProperty(to, property, {
                value: from[property],
                enumerable: forceEnumerable ? true : enumerable,
                configurable: true,
                writable: true
            });
        }
    }
 
    return to;
};
 
const serializeError = value => {
    if (typeof value === 'object' && value !== null) {
        return destroyCircular({from: value, seen: [], forceEnumerable: true});
    }
 
    // People sometimes throw things besides Error objects…
    if (typeof value === 'function') {
        // `JSON.stringify()` discards functions. We do too, unless a function is thrown directly.
        return `[Function: ${(value.name || 'anonymous')}]`;
    }
 
    return value;
};
 
const deserializeError = value => {
    if (value instanceof Error) {
        return value;
    }
 
    if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
        const newError = new Error();
        destroyCircular({from: value, seen: [], to_: newError});
        return newError;
    }
 
    return new NonError(value);
};
 
module.exports = {
    serializeError,
    deserializeError
};