| 1 | import { ProblemDetailsError } from "./ProblemDetailsError.js" |
| 2 | |
| 3 | /** |
| 4 | * Espera a que la promesa de un fetch termine. Si |
| 5 | * hay error, lanza una excepción. |
| 6 | * |
| 7 | * @param {Promise<Response> } servicio |
| 8 | */ |
| 9 | export async function consume(servicio) { |
| 10 | const respuesta = await servicio |
| 11 | if (respuesta.ok) { |
| 12 | return respuesta |
| 13 | } else { |
| 14 | const contentType = respuesta.headers.get("Content-Type") |
| 15 | if ( |
| 16 | contentType !== null && contentType.startsWith("application/problem+json") |
| 17 | ) |
| 18 | throw new ProblemDetailsError(await respuesta.json()) |
| 19 | else |
| 20 | throw new Error(respuesta.statusText) |
| 21 | } |
| 22 | } |
| 1 | /** |
| 2 | * Codifica un texto para que cambie los caracteres |
| 3 | * especiales y no se pueda interpretar como |
| 4 | * etiiqueta HTML. Esta técnica evita la inyección |
| 5 | * de código. |
| 6 | * @param { string } texto |
| 7 | */ |
| 8 | export function htmlentities(texto) { |
| 9 | return texto.replace(/[<>"']/g, textoDetectado => { |
| 10 | switch (textoDetectado) { |
| 11 | case "<": return "<" |
| 12 | case ">": return ">" |
| 13 | case '"': return """ |
| 14 | case "'": return "'" |
| 15 | default: return textoDetectado |
| 16 | } |
| 17 | }) |
| 18 | } |
| 19 |
| 1 | import { muestraError } from "./muestraError.js" |
| 2 | |
| 3 | /** |
| 4 | * Intercepta Response.prototype.json para capturar errores de parseo |
| 5 | * y asegurar que se reporten correctamente en navegadores Chromium. |
| 6 | */ |
| 7 | { |
| 8 | const originalJson = Response.prototype.json |
| 9 | |
| 10 | Response.prototype.json = function () { |
| 11 | // Llamamos al método original usando el contexto (this) de la respuesta |
| 12 | return originalJson.call(this) |
| 13 | .catch((/** @type {any} */ error) => { |
| 14 | // Corrige un error de Chrome que evita el manejo correcto de errores. |
| 15 | throw new Error(error) |
| 16 | }) |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | window.onerror = function ( |
| 21 | /** @type {Event | string} */ _event, |
| 22 | /** @type {string | undefined} */ _fuente, |
| 23 | /** @type {number | undefined} */ _numeroDeLinea, |
| 24 | /** @type {number | undefined} */ _numeroDeColumna, |
| 25 | /** @type {Error | undefined} */ error |
| 26 | ) { |
| 27 | muestraError(error) |
| 28 | return true |
| 29 | } |
| 30 | |
| 31 | window.addEventListener('unhandledrejection', event => { |
| 32 | muestraError(event.reason) |
| 33 | event.preventDefault() |
| 34 | }) |
| 1 | import { ProblemDetailsError } from "./ProblemDetailsError.js" |
| 2 | |
| 3 | /** |
| 4 | * Muestra los datos de una Error en la consola y en un cuadro de alerta. |
| 5 | * @param { unknown } error descripción del error. |
| 6 | */ |
| 7 | export function muestraError(error) { |
| 8 | |
| 9 | if (error instanceof ProblemDetailsError) { |
| 10 | |
| 11 | const problemDetails = error.problemDetails |
| 12 | |
| 13 | let mensaje = |
| 14 | typeof problemDetails["title"] === "string" ? problemDetails["title"] : "" |
| 15 | if (typeof problemDetails["detail"] === "string") { |
| 16 | if (mensaje !== "") { |
| 17 | mensaje += "\n" |
| 18 | } |
| 19 | mensaje += problemDetails["detail"] |
| 20 | } |
| 21 | if (mensaje === "") { |
| 22 | mensaje = "Error" |
| 23 | } |
| 24 | console.error(error, problemDetails) |
| 25 | alert(mensaje) |
| 26 | |
| 27 | } else if ( |
| 28 | typeof error === "object" && error !== null && "message" in error |
| 29 | ) { |
| 30 | |
| 31 | console.error(error) |
| 32 | alert(error.message) |
| 33 | |
| 34 | } else { |
| 35 | |
| 36 | console.error("Error", error) |
| 37 | alert("Error") |
| 38 | |
| 39 | } |
| 40 | |
| 41 | } |
| 1 | export class ProblemDetailsError extends Error { |
| 2 | |
| 3 | /** |
| 4 | * Detalle de los errores devueltos por un servicio. |
| 5 | * Crea una instancia de ProblemDetailsError. |
| 6 | * @param {any} problemDetails Objeto con la descripcipon del error. |
| 7 | */ |
| 8 | constructor(problemDetails) { |
| 9 | |
| 10 | super( |
| 11 | typeof problemDetails["detail"] === "string" |
| 12 | ? problemDetails["detail"] |
| 13 | : ( |
| 14 | typeof problemDetails["title"] === "string" |
| 15 | ? problemDetails["title"] |
| 16 | : "Error" |
| 17 | ) |
| 18 | ) |
| 19 | |
| 20 | this.problemDetails = problemDetails |
| 21 | |
| 22 | } |
| 23 | |
| 24 | } |
| 1 | |
| 2 | /** |
| 3 | * @param {string} url |
| 4 | * @param { "GET" | "POST"| "PUT" | "PATCH" | "DELETE" | "TRACE" | "OPTIONS" |
| 5 | * | "CONNECT" | "HEAD" } metodoHttp |
| 6 | */ |
| 7 | export async function recibeJson(url, metodoHttp = "GET") { |
| 8 | return fetch( |
| 9 | url, |
| 10 | { |
| 11 | method: metodoHttp, |
| 12 | headers: { "Accept": "application/json, application/problem+json" } |
| 13 | } |
| 14 | ) |
| 15 | } |