48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
import { UnauthorizedError } from './unauthorized-error.ts';
|
|
import { ApplicationError } from './application-error.ts';
|
|
|
|
/** Stable error payload shared by HTTP, SSE, and WebSocket. */
|
|
export type PublicError = {
|
|
|
|
/** Protocol-independent status carried by every transport. */
|
|
statusCode: number;
|
|
|
|
/** Client-safe summary which never exposes an unexpected exception. */
|
|
error: string;
|
|
|
|
/** Structured field failures supplied only for validation errors. */
|
|
details?: Array<{ path: string; message: string }>;
|
|
};
|
|
|
|
/**
|
|
* Convert application failures into the common public transport contract.
|
|
*
|
|
* @param error - Any thrown value from a route or transport boundary.
|
|
* @returns A sanitized error payload safe to encode on the wire.
|
|
*/
|
|
export const normalizePublicError = (error: unknown): PublicError => {
|
|
if (error instanceof z.ZodError) {
|
|
return {
|
|
statusCode: 400,
|
|
error: 'Validation Error',
|
|
details: error.issues.map((issue) => ({
|
|
path: issue.path.join('.'),
|
|
message: issue.message,
|
|
})),
|
|
};
|
|
}
|
|
|
|
if (error instanceof UnauthorizedError) {
|
|
return { statusCode: 401, error: error.message };
|
|
}
|
|
|
|
if (error instanceof ApplicationError) {
|
|
return { statusCode: error.statusCode, error: error.message };
|
|
}
|
|
|
|
// Unknown exceptions are logged by adapters, but their messages stay private.
|
|
return { statusCode: 500, error: 'Internal Server Error' };
|
|
};
|