import { codigosResposta, type tipoResposta, type tipoRespostaErro, } from "./respostas" // ─── Tipagem ELicencie ────────────────────────────────────────────── /** * Mapa de códigos HTTP de erro usados pelo sistema ELicencie. * * Single source of truth para derivar `codigoErroELicencie`. */ export const codigosErros = { 400: "Erro de requisição", 403: "Não autenticado", 500: "Erro interno", 504: "Tempo de resposta excedido", } as const /** Códigos HTTP de erro reconhecidos pelo ELicencie. */ export type codigoErroELicencie = keyof typeof codigosErros export interface tipoRespostaSucessoELicencie { codigo: 200 eErro: false valor: T erro: undefined detalhes?: Record } export interface tipoRespostaErroELicencie { codigo: codigoErroELicencie eErro: true erro: string valor: undefined detalhes?: Record } export type tipoRespostaELicencie = | tipoRespostaSucessoELicencie | tipoRespostaErroELicencie // ─── Tipo de entrada (transição) ──────────────────────────────────── /** * Durante a transição de sistema, a entrada pode chegar em qualquer um * dos dois formatos. O conversor detecta e normaliza para P-Resposta. */ export type tipoEntradaTransicao = tipoRespostaELicencie | tipoResposta // ─── Mapeamento bidirecional de códigos ───────────────────────────── /** ELicencie → P-Resposta (403 → 401) */ const mapaCodigoELicencieParaPResposta: Record< codigoErroELicencie, tipoRespostaErro["cod"] > = { 400: codigosResposta.erroConhecido, 403: codigosResposta.erroPermissao, 500: codigosResposta.erroDesconhecido, 504: codigosResposta.tempoEsgotado, } /** P-Resposta → ELicencie (401 → 403, 404 → 400) */ const mapaCodigoPRespostaParaELicencie: Record< tipoRespostaErro["cod"], codigoErroELicencie > = { [codigosResposta.erroConhecido]: 400, [codigosResposta.erroPermissao]: 403, [codigosResposta.erroDesconhecido]: 500, [codigosResposta.erroNaoEncontrado]: 400, [codigosResposta.tempoEsgotado]: 504, } // ─── Type guard ───────────────────────────────────────────────────── /** Detecta se a entrada já está no formato P-Resposta (possui campo `cod`). */ function eRespostaPadrao( entrada: tipoEntradaTransicao, ): entrada is tipoResposta { return "cod" in entrada } // ─── Conversores de detalhes ──────────────────────────────────────── /** `Record` → `string[]` (passthrough se já for array). */ function detalhesParaArray( detalhes?: Record | string[], ): string[] | undefined { if (!detalhes) return undefined if (Array.isArray(detalhes)) return detalhes const entries = Object.entries(detalhes) if (entries.length === 0) return undefined return entries.map(([chave, valor]) => { const valorStr = typeof valor === "string" ? valor : JSON.stringify(valor) return `${chave}: ${valorStr}` }) } // ─── Builder de resposta dual ─────────────────────────────────────── /** * Monta um objeto compatível com **ambos** os formatos em runtime. * * Tipado como `tipoResposta`, mas em runtime também contém * `codigo`, `erro` e `detalhes` (Record) do formato ELicencie. */ function montarRespostaDual(op: { cod: tipoResposta["cod"] valor: T | undefined mensagem: string | undefined eErro: boolean codigoELicencie: 200 | codigoErroELicencie erroELicencie: string | undefined detalhesOriginal?: Record | string[] }): tipoResposta { const detalhesArray = detalhesParaArray(op.detalhesOriginal) const detalhesRecord = op.detalhesOriginal && !Array.isArray(op.detalhesOriginal) ? op.detalhesOriginal : undefined return { // P-Resposta cod: op.cod, valor: op.valor, mensagem: op.mensagem, eErro: op.eErro, eCerto: !op.eErro, detalhes: detalhesArray, // ELicencie (runtime) codigo: op.codigoELicencie, erro: op.erroELicencie, ...(detalhesRecord ? { detalhes: detalhesRecord } : {}), } as tipoResposta } // ─── Conversor principal ──────────────────────────────────────────── /** * Converte uma resposta para o formato **P-Resposta** (padrão do pacote), * mantendo em runtime os campos de **ambos** os formatos para * compatibilidade durante a transição de sistema. * * **Tipo de retorno:** `tipoResposta` * * **Conteúdo em runtime:** campos P-Resposta + campos ELicencie * * | P-Resposta | ELicencie | Relação | * |-------------|-------------|----------------------| * | `cod` | `codigo` | mapeamento de código | * | `mensagem` | `erro` | mesmo conteúdo | * | `valor` | `valor` | direto | * | `eErro` | `eErro` | direto | * | `eCerto` | — | derivado | * | `detalhes` | `detalhes` | string[] / Record | * * @example * ```ts * const res = converterRespostaELicencieParaPResposta(respostaQualquer) * * // Consumidor P-Resposta * if (res.eCerto) console.log(res.valor) * * // Consumidor ELicencie (lê campo extra em runtime) * const resAny = res as any * if (!resAny.eErro) console.log(resAny.valor) * ``` */ export const converterRespostaELicencieParaPResposta = ( entrada: tipoEntradaTransicao, ): tipoResposta => { // ── Entrada já é P-Resposta: enriquecer com campos ELicencie ── if (eRespostaPadrao(entrada)) { if (!entrada.eErro) { return montarRespostaDual({ cod: codigosResposta.sucesso, valor: entrada.valor, mensagem: undefined, eErro: false, codigoELicencie: 200, erroELicencie: undefined, detalhesOriginal: entrada.detalhes, }) } return montarRespostaDual({ cod: entrada.cod, valor: undefined, mensagem: entrada.mensagem, eErro: true, codigoELicencie: mapaCodigoPRespostaParaELicencie[entrada.cod], erroELicencie: entrada.mensagem, detalhesOriginal: entrada.detalhes, }) } // ── Entrada é ELicencie: construir P-Resposta + preservar campos ── if (!entrada.eErro) { return montarRespostaDual({ cod: codigosResposta.sucesso, valor: entrada.valor, mensagem: undefined, eErro: false, codigoELicencie: entrada.codigo, erroELicencie: undefined, detalhesOriginal: entrada.detalhes, }) } return montarRespostaDual({ cod: mapaCodigoELicencieParaPResposta[entrada.codigo], valor: undefined, mensagem: entrada.erro, eErro: true, codigoELicencie: entrada.codigo, erroELicencie: entrada.erro, detalhesOriginal: entrada.detalhes, }) }