persona-community-5/packages/api-client/src/errors.ts
jordan bd2f591b98
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/manual/woodpecker Pipeline was successful
Initialize project from skeleton template
2026-02-24 07:39:46 +00:00

115 lines
3.1 KiB
TypeScript

import type { ApiError, ValidationDetail, ErrorCodeType } from './types';
/**
* API client error with typed error information.
* Provides convenient methods for accessing validation errors.
*/
export class ApiClientError extends Error {
/** HTTP status code */
readonly status: number;
/** Machine-readable error code */
readonly code: string;
/** Optional validation details or other error details */
readonly details?: ValidationDetail[] | Record<string, unknown>;
/** The original response */
readonly response?: Response;
constructor(apiError: ApiError, response?: Response) {
super(apiError.message);
this.name = 'ApiClientError';
this.status = apiError.status;
this.code = apiError.code;
this.details = apiError.details;
this.response = response;
// Maintains proper stack trace in V8 environments
if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, ApiClientError);
}
}
/**
* Check if this is a validation error.
*/
isValidationError(): boolean {
return this.code === 'VALIDATION_ERROR' || this.code === 'BAD_REQUEST';
}
/**
* Check if this is an authentication error.
*/
isAuthError(): boolean {
return this.status === 401 || this.code === 'UNAUTHORIZED';
}
/**
* Check if this is a permission error.
*/
isForbiddenError(): boolean {
return this.status === 403 || this.code === 'FORBIDDEN';
}
/**
* Check if this is a not found error.
*/
isNotFoundError(): boolean {
return this.status === 404 || this.code === 'NOT_FOUND';
}
/**
* Check if the error has a specific code.
*/
hasCode(code: ErrorCodeType | string): boolean {
return this.code === code;
}
/**
* Get validation errors as an array.
* Returns empty array if no validation details are present.
*/
getValidationErrors(): ValidationDetail[] {
if (!this.details) return [];
if (Array.isArray(this.details)) return this.details;
return [];
}
/**
* Get validation errors mapped by field name.
* Useful for displaying errors next to form fields.
*
* @example
* const errors = error.getFieldErrors();
* // { email: 'email is required', password: 'password must be at least 8 characters' }
*/
getFieldErrors(): Record<string, string> {
const errors: Record<string, string> = {};
for (const detail of this.getValidationErrors()) {
errors[detail.field] = detail.message;
}
return errors;
}
/**
* Get the error message for a specific field.
* Returns undefined if no error for that field.
*/
getFieldError(field: string): string | undefined {
const errors = this.getValidationErrors();
return errors.find((e) => e.field === field)?.message;
}
/**
* Check if a specific field has an error.
*/
hasFieldError(field: string): boolean {
return this.getFieldError(field) !== undefined;
}
}
/**
* Type guard for ApiClientError.
*/
export function isApiClientError(error: unknown): error is ApiClientError {
return error instanceof ApiClientError;
}