UNPKG

@smartsamurai/krapi-sdk

Version:

KRAPI TypeScript SDK - Easy-to-use client SDK for connecting to self-hosted KRAPI servers (like Appwrite SDK)

286 lines (266 loc) 7.41 kB
/** * Adapter Error Handler * * Provides centralized error handling for all adapters, ensuring consistent * error transformation and context preservation between client and server modes. */ import { KrapiError } from "../../core/krapi-error"; import { HttpError } from "../../http-clients/http-error"; import { normalizeError, enrichError, createRequestId } from "../../utils/error-handler"; /** * Adapter mode type */ type Mode = "client" | "server"; /** * Handle adapter errors with consistent transformation * * Central error handler for all adapters that transforms errors from HTTP clients * and services into standardized KrapiError format with appropriate context. * * @param error - The error to handle * @param mode - Current adapter mode ("client" or "server") * @param operation - Operation name for context * @param context - Additional context for the error * @returns Never - Always throws the transformed error */ export function handleAdapterError( error: unknown, mode: Mode, operation: string, context?: Record<string, unknown> ): never { let krapiError: KrapiError; // Handle HttpError specifically if (error instanceof HttpError) { krapiError = transformHttpErrorToAdapterError(error, mode, operation, context); } else if (error instanceof KrapiError) { // Already a KrapiError, just enrich with adapter context krapiError = enrichError(error, { adapterMode: mode, operation, ...context, }); } else { // Generic error transformation krapiError = normalizeError(error, "INTERNAL_ERROR", { adapterMode: mode, operation, ...context, }); } // Ensure request ID exists if (!krapiError.requestId) { krapiError = new KrapiError( krapiError.message, krapiError.code, krapiError.status, krapiError.details, createRequestId(), krapiError.cause ); } throw krapiError; } /** * Transform HttpError to adapter-specific KrapiError * * @param httpError - HTTP error to transform * @param mode - Adapter mode * @param operation - Operation name * @param context - Additional context * @returns Transformed KrapiError */ function transformHttpErrorToAdapterError( httpError: HttpError, mode: Mode, operation: string, context?: Record<string, unknown> ): KrapiError { // Import here to avoid circular dependencies // eslint-disable-next-line @typescript-eslint/no-var-requires const { transformHttpError } = require("../../utils/error-handler"); let krapiError = transformHttpError(httpError, { adapterMode: mode, operation, ...context, }); // Add adapter-specific context based on operation type if (operation.includes("auth") || operation.includes("login")) { krapiError = enrichError(krapiError, { category: "authentication", adapterType: "auth", }); } else if (operation.includes("create") || operation.includes("update") || operation.includes("delete")) { krapiError = enrichError(krapiError, { category: "data_modification", adapterType: "data", }); } else if (operation.includes("get") || operation.includes("list") || operation.includes("find")) { krapiError = enrichError(krapiError, { category: "data_retrieval", adapterType: "query", }); } return krapiError; } /** * Create initialization error for adapters * * @param component - Component that failed to initialize * @param mode - Adapter mode * @param reason - Reason for failure * @returns KrapiError for initialization failure */ export function createAdapterInitError( component: string, mode: Mode, reason?: string ): KrapiError { const message = `${component} not initialized. Please ensure you're in ${mode} mode.`; const fullMessage = reason ? `${message} Reason: ${reason}` : message; return new KrapiError( fullMessage, "INTERNAL_ERROR", undefined, { component, mode, reason, initializationError: true, }, createRequestId() ); } /** * Wrap adapter operations with error handling * * Higher-order function that wraps adapter methods with consistent error handling. * * @param operation - Operation function to wrap * @param mode - Adapter mode * @param operationName - Name of the operation * @param contextFn - Function to generate context from arguments * @returns Wrapped operation function */ export function withAdapterErrorHandling<T extends unknown[], R>( operation: (...args: T) => Promise<R>, mode: Mode, operationName: string, contextFn?: (...args: T) => Record<string, unknown> ) { return async (...args: T): Promise<R> => { try { return await operation(...args); } catch (error) { const context = contextFn ? contextFn(...args) : {}; handleAdapterError(error, mode, operationName, context); } }; } /** * Handle mode validation errors * * @param requiredMode - Required mode for operation * @param currentMode - Current adapter mode * @param operation - Operation name * @returns Never - Always throws error */ export function handleModeError( requiredMode: Mode, currentMode: Mode, operation: string ): never { const error = new KrapiError( `Operation '${operation}' requires ${requiredMode} mode, but adapter is in ${currentMode} mode`, "INTERNAL_ERROR", undefined, { requiredMode, currentMode, operation, modeValidationError: true, }, createRequestId() ); throw error; } /** * Validate adapter mode before operation * * @param currentMode - Current adapter mode * @param requiredMode - Required mode * @param operation - Operation name */ export function validateAdapterMode( currentMode: Mode, requiredMode: Mode, operation: string ): void { if (currentMode !== requiredMode) { handleModeError(requiredMode, currentMode, operation); } } /** * Create service operation error * * @param serviceName - Name of the service * @param operation - Operation name * @param mode - Adapter mode * @param originalError - Original error * @returns KrapiError for service operation failure */ export function createServiceOperationError( serviceName: string, operation: string, mode: Mode, originalError?: unknown ): KrapiError { return new KrapiError( `${serviceName} operation '${operation}' failed in ${mode} mode`, "INTERNAL_ERROR", undefined, { serviceName, operation, mode, serviceOperationError: true, originalError, }, createRequestId() ); } /** * Transform service errors to adapter errors * * @param serviceError - Error from service layer * @param serviceName - Name of the service * @param operation - Operation name * @param mode - Adapter mode * @param context - Additional context * @returns Transformed KrapiError */ export function transformServiceError( serviceError: unknown, serviceName: string, operation: string, mode: Mode, context?: Record<string, unknown> ): KrapiError { if (serviceError instanceof KrapiError) { return enrichError(serviceError, { serviceName, operation, mode, layer: "service", ...context, }); } return normalizeError(serviceError, "INTERNAL_ERROR", { serviceName, operation, mode, layer: "service", serviceError: true, ...context, }); }