Uecko_ERP/packages/rdx-ddd/src/errors/domain-validation-error.ts
2025-10-04 17:40:41 +02:00

95 lines
2.6 KiB
TypeScript

import { DomainError } from "./domain-error";
/**
* Error de validación de dominio.
*
* - Extiende DomainError para aprovechar `code`, `metadata`, `cause` y `toJSON`.
* - Estructura estable para mapeo (Problem+JSON / telemetría).
*/
export class DomainValidationError extends DomainError {
/** Discriminante para routing/telemetría */
public readonly kind = "VALIDATION" as const;
/** Regla/identificador de error de validación (ej. INVALID_EMAIL) */
public readonly code: string;
/** Campo afectado (path) */
public readonly field: string;
/** Mensaje legible de negocio */
public readonly detail: string;
constructor(
code: string,
field: string,
detail: string,
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
// Mensaje humano compacto y útil para logs
super(`[${field}] ${detail}`, code, {
...options,
// Aseguramos metadatos ricos y estables
metadata: {
...(options?.metadata ?? {}),
field,
detail,
kind: "VALIDATION",
},
});
this.name = "DomainValidationError";
this.code = code;
this.field = field;
this.detail = detail;
Object.setPrototypeOf(this, new.target.prototype);
Object.freeze(this);
}
/** Atajos de construcción comunes */
static requiredValue(
field: string,
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
return new DomainValidationError("REQUIRED_VALUE", field, "cannot be empty", options);
}
static invalidFormat(
field: string,
detail = "invalid format",
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
return new DomainValidationError("INVALID_FORMAT", field, detail, options);
}
static invalidValue(
field: string,
value: unknown,
detail = "invalid value",
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
// `cause` preserva el valor problemático para inspección sin exponerlo a cliente
return new DomainValidationError("INVALID_VALUE", field, detail, { ...options, cause: value });
}
/** Proyección mínima para Problem+JSON / colecciones */
toDetail() {
return { path: this.field, message: this.detail, rule: this.code };
}
/** Incluye proyección validación en la serialización segura */
override toJSON() {
const base = super.toJSON();
return {
...base,
kind: this.kind,
field: this.field,
detail: this.detail,
};
}
}
/** Type guard */
export const isDomainValidationError = (e: unknown): e is DomainValidationError =>
e instanceof DomainValidationError;