File size: 5,218 Bytes
47b95f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import { printDiffOrStringify } from './diff.js';
import { f as format, s as stringify } from './chunk-_commonjsHelpers.js';
import '@vitest/pretty-format';
import 'tinyrainbow';
import './helpers.js';
import 'loupe';

const IS_RECORD_SYMBOL = "@@__IMMUTABLE_RECORD__@@";
const IS_COLLECTION_SYMBOL = "@@__IMMUTABLE_ITERABLE__@@";
function isImmutable(v) {
	return v && (v[IS_COLLECTION_SYMBOL] || v[IS_RECORD_SYMBOL]);
}
const OBJECT_PROTO = Object.getPrototypeOf({});
function getUnserializableMessage(err) {
	if (err instanceof Error) {
		return `<unserializable>: ${err.message}`;
	}
	if (typeof err === "string") {
		return `<unserializable>: ${err}`;
	}
	return "<unserializable>";
}
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
function serializeValue(val, seen = new WeakMap()) {
	if (!val || typeof val === "string") {
		return val;
	}
	if (val instanceof Error && "toJSON" in val && typeof val.toJSON === "function") {
		const jsonValue = val.toJSON();
		if (jsonValue && jsonValue !== val && typeof jsonValue === "object") {
			if (typeof val.message === "string") {
				safe(() => jsonValue.message ?? (jsonValue.message = val.message));
			}
			if (typeof val.stack === "string") {
				safe(() => jsonValue.stack ?? (jsonValue.stack = val.stack));
			}
			if (typeof val.name === "string") {
				safe(() => jsonValue.name ?? (jsonValue.name = val.name));
			}
			if (val.cause != null) {
				safe(() => jsonValue.cause ?? (jsonValue.cause = serializeValue(val.cause, seen)));
			}
		}
		return serializeValue(jsonValue, seen);
	}
	if (typeof val === "function") {
		return `Function<${val.name || "anonymous"}>`;
	}
	if (typeof val === "symbol") {
		return val.toString();
	}
	if (typeof val !== "object") {
		return val;
	}
	if (typeof Buffer !== "undefined" && val instanceof Buffer) {
		return `<Buffer(${val.length}) ...>`;
	}
	if (typeof Uint8Array !== "undefined" && val instanceof Uint8Array) {
		return `<Uint8Array(${val.length}) ...>`;
	}
	// cannot serialize immutables as immutables
	if (isImmutable(val)) {
		return serializeValue(val.toJSON(), seen);
	}
	if (val instanceof Promise || val.constructor && val.constructor.prototype === "AsyncFunction") {
		return "Promise";
	}
	if (typeof Element !== "undefined" && val instanceof Element) {
		return val.tagName;
	}
	if (typeof val.asymmetricMatch === "function") {
		return `${val.toString()} ${format(val.sample)}`;
	}
	if (typeof val.toJSON === "function") {
		return serializeValue(val.toJSON(), seen);
	}
	if (seen.has(val)) {
		return seen.get(val);
	}
	if (Array.isArray(val)) {
		// eslint-disable-next-line unicorn/no-new-array -- we need to keep sparse arrays ([1,,3])
		const clone = new Array(val.length);
		seen.set(val, clone);
		val.forEach((e, i) => {
			try {
				clone[i] = serializeValue(e, seen);
			} catch (err) {
				clone[i] = getUnserializableMessage(err);
			}
		});
		return clone;
	} else {
		// Objects with `Error` constructors appear to cause problems during worker communication
		// using `MessagePort`, so the serialized error object is being recreated as plain object.
		const clone = Object.create(null);
		seen.set(val, clone);
		let obj = val;
		while (obj && obj !== OBJECT_PROTO) {
			Object.getOwnPropertyNames(obj).forEach((key) => {
				if (key in clone) {
					return;
				}
				try {
					clone[key] = serializeValue(val[key], seen);
				} catch (err) {
					// delete in case it has a setter from prototype that might throw
					delete clone[key];
					clone[key] = getUnserializableMessage(err);
				}
			});
			obj = Object.getPrototypeOf(obj);
		}
		return clone;
	}
}
function safe(fn) {
	try {
		return fn();
	} catch {}
}
function normalizeErrorMessage(message) {
	return message.replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "");
}
function processError(_err, diffOptions, seen = new WeakSet()) {
	if (!_err || typeof _err !== "object") {
		return { message: String(_err) };
	}
	const err = _err;
	if (err.showDiff || err.showDiff === undefined && err.expected !== undefined && err.actual !== undefined) {
		err.diff = printDiffOrStringify(err.actual, err.expected, {
			...diffOptions,
			...err.diffOptions
		});
	}
	if ("expected" in err && typeof err.expected !== "string") {
		err.expected = stringify(err.expected, 10);
	}
	if ("actual" in err && typeof err.actual !== "string") {
		err.actual = stringify(err.actual, 10);
	}
	// some Error implementations don't allow rewriting message
	try {
		if (typeof err.message === "string") {
			err.message = normalizeErrorMessage(err.message);
		}
	} catch {}
	// some Error implementations may not allow rewriting cause
	// in most cases, the assignment will lead to "err.cause = err.cause"
	try {
		if (!seen.has(err) && typeof err.cause === "object") {
			seen.add(err);
			err.cause = processError(err.cause, diffOptions, seen);
		}
	} catch {}
	try {
		return serializeValue(err);
	} catch (e) {
		return serializeValue(new Error(`Failed to fully serialize error: ${e === null || e === void 0 ? void 0 : e.message}\nInner error message: ${err === null || err === void 0 ? void 0 : err.message}`));
	}
}

export { processError, serializeValue as serializeError, serializeValue };