export type ExceptionCode = string | number; export interface ExceptionsProps { /** * Custom exception code */ code?: ExceptionCode | undefined; /** * Wrapped original error */ cause?: unknown | undefined; /** * Additional structured details */ details?: T | undefined; } /** * Every error thrown in platform based application * should be derivitives of PlatformException. This doesn't * mean that thowing just new Error() will ruin production. * But there will be no automatic nice handling in this case. */ export abstract class PlatformException extends Error { /** * Class name of exception */ readonly metatype: string; /** * Required! */ readonly code: ExceptionCode; /** * PlatformException has ability to wrap original errors. Such an option exists * in many other languages and frameworks: .NET, Java, Go etc. */ override readonly cause: unknown | undefined; /** * Any custom attributes developer is willing to * attach. Very useful for creating structured validation * errors */ readonly details: T | undefined; /** * @param code * @param message error message * @param props additional properties */ constructor( code: ExceptionCode, message: string, props?: Omit, 'code'>, ) { super(message); this.metatype = this.constructor.name; this.code = code; this.cause = props?.cause; this.details = props?.details; } static innerMessages(ex: Error): string[] { if ( !ex || !(ex instanceof PlatformException) || !ex.cause || !(ex.cause instanceof Error) ) { return []; } return [ex.cause.message, ...PlatformException.innerMessages(ex.cause)]; } }