UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

139 lines (127 loc) 3.63 kB
/** * Framework-agnostic error mapping utilities for @beignet/core/server */ import { createErrorResponseBody, type ErrorResponseBody, } from "../../errors/index.js"; import type { AppEnvironment } from "../health.js"; import { getRequestIdFromContext } from "./utils.js"; /** * Re-export `AppEnvironment` for convenience. */ export type { AppEnvironment } from "../health.js"; /** * Framework-neutral response produced by an error mapper. */ export interface ErrorMappingResult { /** * HTTP status code. */ status: number; /** * Response body. */ body: unknown; /** * Response headers. */ headers?: Record<string, string>; } /** * Error mapping configuration. */ export interface ErrorMappingConfig<Ctx> { /** Custom error mapper function. */ mapErrorToResponse?: (err: unknown, ctx: Ctx) => ErrorMappingResult; /** Include stack traces in error responses. Defaults to true in dev/test. */ includeStackInResponse?: boolean; /** Application environment. */ env?: AppEnvironment; } /** * Create default error response body */ function createDefaultErrorBody( err: unknown, includeStack: boolean, requestId?: string, ): ErrorResponseBody { return createErrorResponseBody({ code: "INTERNAL_SERVER_ERROR", message: "Internal server error", requestId, details: includeStack && err instanceof Error ? { error: { message: err.message, stack: err.stack, }, } : undefined, }); } /** * Default error mapping function that handles unknown errors and converts them * to a standard error response format. * * **Important:** This function does NOT handle AppError instances from @beignet/core/errors. * AppError is handled separately in the router's error handling flow before reaching * this function. This function is only called for truly unexpected errors that bypass normal * error handling (e.g., unhandled exceptions, infrastructure errors). * * This function: * 1. First tries the custom mapErrorToResponse if provided * 2. Falls back to a default 500 error response * 3. Optionally includes stack traces in development/test environments * * @param err - The error that was thrown (excluding AppError instances) * @param ctx - The request context * @param config - Error mapping configuration * @returns An error mapping result with status, body, and optional headers * * @example * ```ts * const errorConfig = { * mapErrorToResponse: (err, ctx) => ({ * status: 500, * body: { * code: "INTERNAL_SERVER_ERROR", * message: "Custom error", * requestId: ctx.requestId, * }, * }), * includeStackInResponse: true, * env: "development", * }; * * const result = defaultMapErrorToResponse(error, ctx, errorConfig); * ``` */ export function defaultMapErrorToResponse<Ctx>( err: unknown, ctx: Ctx, config: ErrorMappingConfig<Ctx>, ): ErrorMappingResult { // First, try the user's custom error handler if (config.mapErrorToResponse) { try { return config.mapErrorToResponse(err, ctx); } catch { // Fall through to default error response below } } // Determine if stack traces should be included const includeStack = config.includeStackInResponse ?? (config.env === "development" || config.env === "test"); // Default error response const requestId = getRequestIdFromContext(ctx); const body = createDefaultErrorBody(err, includeStack, requestId); return { status: 500, body, headers: { "Content-Type": "application/json" }, }; }