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;
|