@hirosystems/api-toolkit
Version:
API development toolkit
135 lines • 4.82 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
Object.defineProperty(exports, "__esModule", { value: true });
exports.addKnownErrorConstructor = addKnownErrorConstructor;
exports.isErrorLike = isErrorLike;
exports.serializeError = serializeError;
exports.deserializeError = deserializeError;
const errorConstructors = new Map([
// Native ES errors https://262.ecma-international.org/12.0/#sec-well-known-intrinsic-objects
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError,
AggregateError,
// Built-in errors
globalThis.DOMException,
// Node-specific errors https://nodejs.org/api/errors.html
globalThis.AssertionError,
globalThis.SystemError,
]
// Non-native Errors are used with `globalThis` because they might be missing. This filter drops them when undefined.
.filter(Boolean)
.map(constructor => [constructor.name, constructor]));
/**
* Custom errors can only be deserialized correctly if they are registered here.
*/
function addKnownErrorConstructor(constructor) {
try {
new constructor();
}
catch (error) {
throw new Error(`The error constructor "${constructor.name}" is not compatible`, {
cause: error,
});
}
errorConstructors.set(constructor.name, constructor);
}
const commonProperties = {
name: false,
message: false,
stack: false,
code: true,
cause: false,
errors: false,
};
function isErrorLike(value) {
return (typeof value === 'object' &&
value !== null &&
'name' in value &&
'message' in value &&
'stack' in value &&
typeof value.name === 'string' &&
typeof value.message === 'string' &&
typeof value.stack === 'string');
}
function serializeError(subject) {
if (!isErrorLike(subject)) {
// If the subject is not an error, for example `throw "boom", then we throw.
// This function should only be passed error objects, callers can use `isErrorLike`.
throw new TypeError('Failed to serialize error, expected an error object');
}
const data = {
constructorName: subject.constructor.name ?? 'Error', // new field
name: subject.name,
message: '',
stack: '',
};
for (const key of Object.keys(commonProperties)) {
if (key in subject)
data[key] = deepSerialize(subject[key]);
}
// Include any other enumerable own properties
for (const key of Object.keys(subject)) {
if (!(key in data))
data[key] = deepSerialize(subject[key]);
}
return data;
}
function deserializeError(subject) {
if (!isErrorLike(subject)) {
// If the subject is not an error, for example `throw "boom", then we throw.
// This function should only be passed error objects, callers can use `isErrorLike`.
throw new TypeError('Failed to desserialize error, expected an error object');
}
let con = errorConstructors.get(subject.constructorName ?? subject.name);
if (!con) {
// If the constructor is not found, use the generic Error constructor
con = Error;
console.error(`Error constructor "${subject.name}" not found during worker error deserialization, using generic Error constructor`);
}
const output = Object.create(con.prototype);
for (const [key, enumerable] of Object.entries(commonProperties)) {
if (key in subject) {
Object.defineProperty(output, key, {
enumerable,
configurable: true,
writable: true,
value: deepDeserialize(subject[key]),
});
}
}
// Add any other properties (custom props not in commonProperties)
for (const key of Object.keys(subject)) {
if (!(key in commonProperties)) {
output[key] = deepDeserialize(subject[key]);
}
}
return output;
}
function deepSerialize(value) {
if (Array.isArray(value))
return value.map(deepSerialize);
if (isErrorLike(value))
return serializeError(value);
if (value && typeof value === 'object') {
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deepSerialize(v)]));
}
return value;
}
function deepDeserialize(value) {
if (Array.isArray(value))
return value.map(deepDeserialize);
if (isErrorLike(value))
return deserializeError(value);
if (value && typeof value === 'object') {
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deepDeserialize(v)]));
}
return value;
}
//# sourceMappingURL=serialize-error.js.map