import type { inferClientTypes, InferrableClientTypes, Maybe, TRPCErrorResponse, } from '@trpc/server/unstable-core-do-not-import'; import { isObject, type DefaultErrorShape, } from '@trpc/server/unstable-core-do-not-import'; type inferErrorShape = inferClientTypes['errorShape']; export interface TRPCClientErrorBase { readonly message: string; readonly shape: Maybe; readonly data: Maybe; } export type TRPCClientErrorLike = TRPCClientErrorBase>; export function isTRPCClientError( cause: unknown, ): cause is TRPCClientError { return cause instanceof TRPCClientError; } function isTRPCErrorResponse(obj: unknown): obj is TRPCErrorResponse { return ( isObject(obj) && isObject(obj['error']) && typeof obj['error']['code'] === 'number' && typeof obj['error']['message'] === 'string' ); } function getMessageFromUnknownError(err: unknown, fallback: string): string { if (typeof err === 'string') { return err; } if (isObject(err) && typeof err['message'] === 'string') { return err['message']; } return fallback; } export class TRPCClientError extends Error implements TRPCClientErrorBase> { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore override doesn't work in all environments due to "This member cannot have an 'override' modifier because it is not declared in the base class 'Error'" public override readonly cause; public readonly shape: Maybe>; public readonly data: Maybe['data']>; /** * Additional meta data about the error * In the case of HTTP-errors, we'll have `response` and potentially `responseJSON` here */ public meta; constructor( message: string, opts?: { result?: Maybe>>; cause?: Error; meta?: Record; }, ) { const cause = opts?.cause; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore https://github.com/tc39/proposal-error-cause super(message, { cause }); this.meta = opts?.meta; this.cause = cause; this.shape = opts?.result?.error; this.data = opts?.result?.error.data; this.name = 'TRPCClientError'; Object.setPrototypeOf(this, TRPCClientError.prototype); } public static from( _cause: Error | TRPCErrorResponse | object, opts: { meta?: Record; cause?: Error } = {}, ): TRPCClientError { const cause = _cause as unknown; if (isTRPCClientError(cause)) { if (opts.meta) { // Decorate with meta error data cause.meta = { ...cause.meta, ...opts.meta, }; } return cause; } if (isTRPCErrorResponse(cause)) { return new TRPCClientError(cause.error.message, { ...opts, result: cause, cause: opts.cause, }); } return new TRPCClientError( getMessageFromUnknownError(cause, 'Unknown error'), { ...opts, cause: cause as any, }, ); } }