mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 12:43:18 +00:00
Incluye ambos changesets 032 (document-get y work-program) en el changelog. Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
111 lines
3.3 KiB
TypeScript
111 lines
3.3 KiB
TypeScript
export type FrontRpcCode = "VALIDATION" | "NETWORK" | "INTERNAL";
|
|
|
|
export type FrontResponse<T = unknown> = {
|
|
ok: boolean;
|
|
code: FrontRpcCode | string;
|
|
layer: "front";
|
|
message: string;
|
|
context?: Record<string, unknown>;
|
|
data?: T | null;
|
|
errors?: Record<string, string> | string[] | null;
|
|
};
|
|
|
|
export function buildFrontError(
|
|
message: string,
|
|
code: FrontRpcCode = "VALIDATION",
|
|
context?: Record<string, unknown>,
|
|
errors?: Record<string, string> | string[] | null,
|
|
): FrontResponse {
|
|
return {
|
|
ok: false,
|
|
code,
|
|
layer: "front",
|
|
message,
|
|
context,
|
|
data: null,
|
|
errors: errors ?? null,
|
|
};
|
|
}
|
|
|
|
export function buildFrontNetworkError(method: string, path: string): FrontResponse {
|
|
return buildFrontError(
|
|
`No se pudo conectar con el servidor en ${method} ${path} — verifique su conexión`,
|
|
"NETWORK",
|
|
{ route: `${method} ${path}` },
|
|
);
|
|
}
|
|
|
|
export function isApiEnvelope(value: unknown): value is {
|
|
ok: boolean;
|
|
code: string;
|
|
status?: number;
|
|
layer: string;
|
|
message: string;
|
|
data?: unknown;
|
|
} {
|
|
return !!value && typeof value === "object"
|
|
&& "ok" in value && "message" in value && "layer" in value;
|
|
}
|
|
|
|
/** Normaliza respuestas `{ ok, data }` (con o sin envelope RPC) al shape plano que esperan las pantallas. */
|
|
export function unwrapApiPayload<T>(body: unknown): T {
|
|
if (!body || typeof body !== "object") return body as T;
|
|
const rec = body as Record<string, unknown>;
|
|
const data = rec.data;
|
|
const canSpread = "ok" in rec
|
|
&& data != null
|
|
&& typeof data === "object"
|
|
&& !Array.isArray(data);
|
|
if (!isApiEnvelope(body) && !canSpread) return body as T;
|
|
const spread = canSpread ? data as Record<string, unknown> : {};
|
|
return {
|
|
...spread,
|
|
data,
|
|
ok: rec.ok,
|
|
code: rec.code,
|
|
message: rec.message,
|
|
status: rec.status,
|
|
layer: rec.layer,
|
|
} as T;
|
|
}
|
|
|
|
function firstListedError(value: object): string | null {
|
|
if (!("errors" in value) || value.errors == null) return null;
|
|
const errors = (value as { errors: unknown }).errors;
|
|
if (typeof errors === "string" && errors.trim()) return errors;
|
|
if (!Array.isArray(errors)) return null;
|
|
for (const item of errors) {
|
|
if (typeof item === "string" && item.trim()) return item;
|
|
if (item && typeof item === "object" && "messages" in item) {
|
|
const msgs = (item as { messages: unknown }).messages;
|
|
if (Array.isArray(msgs)) {
|
|
const first = msgs.find((msg) => typeof msg === "string" && msg.trim());
|
|
if (typeof first === "string") return first;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** Muestra el mensaje detallado de la API o front sin reescribirlo. */
|
|
export function displayErrorMessage(value: unknown, fallback = "Ocurrió un error"): string {
|
|
if (isApiEnvelope(value)) {
|
|
const listed = firstListedError(value);
|
|
const message = value.message?.trim();
|
|
if (message) return message;
|
|
if (listed) return listed;
|
|
}
|
|
if (value && typeof value === "object") {
|
|
if ("message" in value) {
|
|
const message = String((value as { message: unknown }).message || "").trim();
|
|
if (message) return message;
|
|
}
|
|
if ("error" in value) {
|
|
const error = String((value as { error: unknown }).error || "").trim();
|
|
if (error) return error;
|
|
}
|
|
const listed = firstListedError(value);
|
|
if (listed) return listed;
|
|
}
|
|
return fallback || "Ocurrió un error";
|
|
}
|