Uecko_ERP/packages/rdx-ddd/src/errors/domain-validation-error.ts

95 lines
2.6 KiB
TypeScript
Raw Normal View History

2025-10-04 15:40:41 +00:00
import { DomainError } from "./domain-error";
2025-08-25 17:42:56 +00:00
/**
2025-10-04 15:40:41 +00:00
* Error de validación de dominio.
2025-08-25 17:42:56 +00:00
*
2025-10-04 15:40:41 +00:00
* - Extiende DomainError para aprovechar `code`, `metadata`, `cause` y `toJSON`.
* - Estructura estable para mapeo (Problem+JSON / telemetría).
2025-08-25 17:42:56 +00:00
*/
export class DomainValidationError extends DomainError {
2025-10-04 15:40:41 +00:00
/** Discriminante para routing/telemetría */
2025-08-25 17:42:56 +00:00
public readonly kind = "VALIDATION" as const;
2025-10-04 15:40:41 +00:00
/** 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;
2025-08-25 17:42:56 +00:00
constructor(
2025-10-04 15:40:41 +00:00
code: string,
field: string,
detail: string,
options?: ErrorOptions & { metadata?: Record<string, unknown> }
2025-08-25 17:42:56 +00:00
) {
2025-10-04 15:40:41 +00:00
// Mensaje humano compacto y útil para logs
super(`[${field}] ${detail}`, code, {
...options,
// Aseguramos metadatos ricos y estables
metadata: {
...(options?.metadata ?? {}),
field,
detail,
kind: "VALIDATION",
},
});
2025-08-25 17:42:56 +00:00
this.name = "DomainValidationError";
2025-10-04 15:40:41 +00:00
this.code = code;
this.field = field;
this.detail = detail;
Object.setPrototypeOf(this, new.target.prototype);
2025-08-25 17:42:56 +00:00
Object.freeze(this);
}
2025-10-04 15:40:41 +00:00
/** Atajos de construcción comunes */
static requiredValue(
field: string,
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
2025-09-16 11:29:45 +00:00
return new DomainValidationError("REQUIRED_VALUE", field, "cannot be empty", options);
2025-08-25 17:42:56 +00:00
}
2025-10-04 15:40:41 +00:00
static invalidFormat(
field: string,
detail = "invalid format",
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
2025-08-25 17:42:56 +00:00
return new DomainValidationError("INVALID_FORMAT", field, detail, options);
}
2025-10-04 15:40:41 +00:00
2025-09-16 11:29:45 +00:00
static invalidValue(
field: string,
value: unknown,
detail = "invalid value",
2025-10-04 15:40:41 +00:00
options?: ErrorOptions & { metadata?: Record<string, unknown> }
2025-09-16 11:29:45 +00:00
) {
2025-10-04 15:40:41 +00:00
// `cause` preserva el valor problemático para inspección sin exponerlo a cliente
2025-09-16 11:29:45 +00:00
return new DomainValidationError("INVALID_VALUE", field, detail, { ...options, cause: value });
}
2025-08-25 17:42:56 +00:00
2025-10-04 15:40:41 +00:00
/** Proyección mínima para Problem+JSON / colecciones */
2025-08-25 17:42:56 +00:00
toDetail() {
return { path: this.field, message: this.detail, rule: this.code };
}
2025-10-04 15:40:41 +00:00
/** 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,
};
}
2025-08-25 17:42:56 +00:00
}
2025-10-04 15:40:41 +00:00
/** Type guard */
2025-08-25 17:42:56 +00:00
export const isDomainValidationError = (e: unknown): e is DomainValidationError =>
e instanceof DomainValidationError;