{"_id":"@alijunior/acbr-api-sdk-node","_rev":"2-663315d79109eb9ec3b69038b4133294","name":"@alijunior/acbr-api-sdk-node","dist-tags":{"latest":"2.3.6"},"versions":{"2.3.4":{"name":"@alijunior/acbr-api-sdk-node","version":"2.3.4","keywords":["acbr","api","acbrapi","acbr-api","acbr api","sdk","node","typescript","nfe","nfce"],"author":{"url":"https://github.com/alijuniorbr","name":"Junior Ali","email":"alijunior@gmail.com"},"license":"MIT","_id":"@alijunior/acbr-api-sdk-node@2.3.4","maintainers":[{"name":"alijunior","email":"alijunior@gmail.com"}],"homepage":"https://github.com/alijuniorbr/acbr-api-sdk-node#readme","bugs":{"url":"https://github.com/alijuniorbr/acbr-api-sdk-node/issues"},"dist":{"shasum":"8b8ecebf0cf30ebafba30068b540e23f3907176c","tarball":"https://registry.npmjs.org/@alijunior/acbr-api-sdk-node/-/acbr-api-sdk-node-2.3.4.tgz","fileCount":6,"integrity":"sha512-7ZwLAP2b4BTLkw5SDPgcE8I1k8h8T50BEEB6d8fphX+vzmJZoStd2xm0yur0pDPPm1A+wc3jPrQ2PnyRS1rDFQ==","signatures":[{"sig":"MEQCICIdFIaaFneq2VBNsYjCVd7V9K5cIzhgVjoXwFyxBx2KAiBOwDgjKpPNdtMknsJuHKxkcubVKkpDV8S5PGFn9Vgnrw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1429898},"main":"./dist/index.js","type":"commonjs","_from":"file:alijunior-acbr-api-sdk-node-2.3.4.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"bump":"pnpm version patch --no-git-tag-version","lint":"eslint .","test":"dotenv -e .env -- tsx ./run/acbr-api-run-example.ts","tree":"tree -a -I 'node_modules|.git|dist|build|coverage|.turbo|.next|.cache' > content-tree.txt","build":"tsup index.ts --format esm,cjs --dts --out-dir dist --clean","clean":"rm -rf dist","headers":"tsx scripts/add-file-headers.ts","lint:fix":"eslint . --fix"},"_npmUser":{"name":"alijunior","email":"alijunior@gmail.com"},"_resolved":"/private/var/folders/04/5j3j45wj4lx0xg3snk75q22w0000gn/T/4bfc3e1b966a536599d97e7705685eeb/alijunior-acbr-api-sdk-node-2.3.4.tgz","_integrity":"sha512-7ZwLAP2b4BTLkw5SDPgcE8I1k8h8T50BEEB6d8fphX+vzmJZoStd2xm0yur0pDPPm1A+wc3jPrQ2PnyRS1rDFQ==","repository":{"url":"git+https://github.com/alijuniorbr/acbr-api-sdk-node.git","type":"git"},"_npmVersion":"11.5.2","description":"SDK em Typescript para comunicação com a api Acbr Api","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","dependencies":{"axios":"^1.13.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.1","eslint":"^10.0.2","dotenv-cli":"^11.0.0","typescript":"^5.9.3","@types/node":"^24.10.1","@antfu/eslint-config":"^7.5.0","eslint-plugin-format":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/acbr-api-sdk-node_2.3.4_1771974458130_0.04211260280793305","host":"s3://npm-registry-packages-npm-production"}},"2.3.6":{"name":"@alijunior/acbr-api-sdk-node","version":"2.3.6","description":"SDK em Typescript para comunicação com a api Acbr Api","keywords":["acbr","api","acbrapi","acbr-api","acbr api","sdk","node","typescript","nfe","nfce"],"homepage":"https://github.com/alijuniorbr/acbr-api-sdk-node#readme","bugs":{"url":"https://github.com/alijuniorbr/acbr-api-sdk-node/issues"},"repository":{"type":"git","url":"git+https://github.com/alijuniorbr/acbr-api-sdk-node.git"},"license":"MIT","author":{"name":"Junior Ali","email":"alijunior@gmail.com","url":"https://github.com/alijuniorbr"},"type":"commonjs","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"sideEffects":false,"engines":{"node":">=18"},"publishConfig":{"access":"public"},"dependencies":{"axios":"^1.13.2"},"devDependencies":{"@antfu/eslint-config":"^7.5.0","@types/node":"^24.10.1","dotenv-cli":"^11.0.0","eslint":"^10.0.2","eslint-plugin-format":"^2.0.0","tsup":"^8.5.1","tsx":"^4.20.6","typescript":"^5.9.3"},"scripts":{"build":"tsup index.ts --format esm,cjs --dts --out-dir dist --clean","clean":"rm -rf dist","test":"dotenv -e .env -- tsx ./run/acbr-api-run-example.ts","tree":"tree -a -I 'node_modules|.git|dist|build|coverage|.turbo|.next|.cache' > content-tree.txt","lint":"eslint .","lint:fix":"eslint . --fix","bump":"pnpm version patch --no-git-tag-version","headers":"tsx scripts/add-file-headers.ts"},"_id":"@alijunior/acbr-api-sdk-node@2.3.6","_integrity":"sha512-/oC5MSjqIzTq1hoOIrwRIEPnRo2KTvbLZuZGlG0FeBuuhazT2s5VecpXvQ9yuvr4JJ4DvqMLWt+Kkb4bxpkkNQ==","_resolved":"/private/var/folders/04/5j3j45wj4lx0xg3snk75q22w0000gn/T/76183cd7be0afdaab09787ee8195f6d8/alijunior-acbr-api-sdk-node-2.3.6.tgz","_from":"file:alijunior-acbr-api-sdk-node-2.3.6.tgz","_nodeVersion":"22.14.0","_npmVersion":"11.5.2","dist":{"integrity":"sha512-/oC5MSjqIzTq1hoOIrwRIEPnRo2KTvbLZuZGlG0FeBuuhazT2s5VecpXvQ9yuvr4JJ4DvqMLWt+Kkb4bxpkkNQ==","shasum":"8ad713efcf6f1241f5ef41025e4e4610293a0b69","tarball":"https://registry.npmjs.org/@alijunior/acbr-api-sdk-node/-/acbr-api-sdk-node-2.3.6.tgz","fileCount":6,"unpackedSize":1494781,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIG991QYarqq6jmTFgxgDH7cDpFVD8trPAfNenFZCfacUAiAPQUxieQefzMq0I7vbb4S944g1DkgrHoZZMnK5HrXzzw=="}]},"_npmUser":{"name":"alijunior","email":"alijunior@gmail.com"},"directories":{},"maintainers":[{"name":"alijunior","email":"alijunior@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/acbr-api-sdk-node_2.3.6_1783009813158_0.13308576086118484"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-24T23:07:38.021Z","modified":"2026-07-02T16:30:13.512Z","2.3.4":"2026-02-24T23:07:38.378Z","2.3.6":"2026-07-02T16:30:13.411Z"},"bugs":{"url":"https://github.com/alijuniorbr/acbr-api-sdk-node/issues"},"author":{"name":"Junior Ali","email":"alijunior@gmail.com","url":"https://github.com/alijuniorbr"},"license":"MIT","homepage":"https://github.com/alijuniorbr/acbr-api-sdk-node#readme","keywords":["acbr","api","acbrapi","acbr-api","acbr api","sdk","node","typescript","nfe","nfce"],"repository":{"type":"git","url":"git+https://github.com/alijuniorbr/acbr-api-sdk-node.git"},"description":"SDK em Typescript para comunicação com a api Acbr Api","maintainers":[{"name":"alijunior","email":"alijunior@gmail.com"}],"readme":"<h1 align=\"center\">@alijunior/acbr-api-sdk-node</h1>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@alijunior/acbr-api-sdk-node\">\n    <img alt=\"npm version\" src=\"https://img.shields.io/npm/v/@alijunior/acbr-api-sdk-node.svg\">\n  </a>\n  <a href=\"https://www.npmjs.com/package/@alijunior/acbr-api-sdk-node\">\n    <img alt=\"npm downloads\" src=\"https://img.shields.io/npm/dw/@alijunior/acbr-api-sdk-node.svg\">\n  </a>\n  <img alt=\"license\" src=\"https://img.shields.io/npm/l/@alijunior/acbr-api-sdk-node.svg\">\n\n</p>\n<h3>Veja também o SDK para a Nuvem Fiscal \n  <a href=\"https://github.com/alijuniorbr/nuvemfiscal-sdk-node\">\n    @alijunior/nuvemfiscal-sdk-node\n  </a>\n</h3>\n<br />\n\nSDK em **TypeScript/Node.js** para integração com a **Acbr API**, com tipos gerados a partir do OpenAPI e client HTTP enxuto (fetch/axios).\n\n> ✅ Destaques\n>\n> - Tipagem forte (params, body e response) **direto do OpenAPI**\n> - Client simples: `AcbrApi`\n> - Adapters: `FetchHttpClient` (nativo) e `AxiosHttpClient` (opcional)\n> - **Helpers de token**:\n>   - `getClientCredentialsToken` (objeto completo do token)\n>   - `getAccessTokenString` (só a string do token; wrapper)\n>   - `getAcbrApiTokenForBrowser` (para **testes no browser** — não usar em produção)\n\n> 🤖 **Usando com assistentes de IA?** Este pacote inclui um arquivo [`llms.txt`](./llms.txt) com contexto pronto sobre como aplicar o SDK no backend e no browser. Aponte seu assistente para ele.\n\n> 💡 Este SDK é **gêmeo** do [`@alijunior/nuvemfiscal-sdk-node`](https://github.com/alijuniorbr/nuvemfiscal-sdk-node): mesma interface, mesmos adapters e mesmos helpers de token. Se você já usa um, o outro funciona do mesmo jeito — trocando apenas o nome do client (`AcbrApi` ⇆ `NuvemFiscalApi`), o `tokenUrl` e o `API_BASE`.\n\n---\n\n## Instalação\n\n```bash\npnpm add @alijunior/acbr-api-sdk-node\n```\n\nou\n\n```bash\nnpm i @alijunior/acbr-api-sdk-node\n```\n\nou\n\n```bash\nyarn add @alijunior/acbr-api-sdk-node\n```\n\n### Requisitos\n\n- Node **>= 18** (fetch nativo)\n- TypeScript **>= 5** (recomendado)\n\n---\n\n## Variáveis de ambiente (para exemplos)\n\nCrie um `.env` na raiz do seu projeto:\n\n```dotenv\nTOKEN_URL=https://auth.acbr.api.br/realms/ACBrAPI/protocol/openid-connect/token\nCLIENT_ID=SEU_CLIENT_ID\nCLIENT_SECRET=SEU_CLIENT_SECRET\nAPI_BASE=https://prod.acbr.api.br/\nSCOPE=empresa conta cnpj cep nfce nfe\n```\n\n> A autenticação da Acbr API usa **Keycloak** (`auth.acbr.api.br`). O endpoint de token é o padrão OpenID Connect: `.../realms/ACBrAPI/protocol/openid-connect/token`.\n\n---\n\n## Helpers de Token\n\n### 1) `getClientCredentialsToken` (Node – objeto completo)\n\nFluxo **OAuth2 Client Credentials**. Retorna `{ access_token, expires_in, ... }`.  \nSuporta `authStyle`: `\"basic\" | \"body\" | \"both\" | \"auto\"` (padrão: `\"auto\"`).\n\n```ts\nimport {\n  getClientCredentialsToken,\n  type ClientCredentialsInput,\n} from \"@alijunior/acbr-api-sdk-node\";\n\nconst input: ClientCredentialsInput = {\n  tokenUrl: process.env.TOKEN_URL!,\n  clientId: process.env.CLIENT_ID!,\n  clientSecret: process.env.CLIENT_SECRET!,\n  scope: process.env.SCOPE, // opcional\n  authStyle: \"auto\", // tenta basic e cai para Body em 400/401\n};\n\nconst token = await getClientCredentialsToken(input);\nconsole.log(\"access_token:\", token.access_token, \"exp:\", token.expires_in);\n```\n\n### 2) `getAccessTokenString` (Node – apenas a string)\n\nWrap prático quando você só precisa do `access_token`.\n\n```ts\nimport { getAccessTokenString } from \"@alijunior/acbr-api-sdk-node\";\n\nconst accessToken = await getAccessTokenString({\n  tokenUrl: process.env.TOKEN_URL!,\n  clientId: process.env.CLIENT_ID!,\n  clientSecret: process.env.CLIENT_SECRET!,\n  scope: process.env.SCOPE, // opcional\n});\n```\n\n### 3) `getAcbrApiTokenForBrowser` (Browser – **somente testes**)\n\nÚtil para provar o fluxo **localmente**. **Não use em produção** (expõe o `client_secret`).\n\nAssinatura: `getAcbrApiTokenForBrowser(clientId, clientSecret, tokenUrl?, scope?)`. Os parâmetros `tokenUrl` e `scope` são **opcionais** e posicionais.\n\n> ⚠️ **Atenção (CORS):** o servidor de autenticação Keycloak da Acbr API normalmente **não libera CORS** para origens de browser. Na prática, mesmo em testes, a obtenção de token direto do navegador pode ser bloqueada. O caminho confiável é obter o token **no backend** e expô-lo ao front (veja a seção sobre browser em produção, mais abaixo).\n\n```ts\nimport { getAcbrApiTokenForBrowser } from \"@alijunior/acbr-api-sdk-node\";\n\nconst accessToken = await getAcbrApiTokenForBrowser(\n  \"CLIENT_ID\",\n  \"CLIENT_SECRET\",\n  \"https://auth.acbr.api.br/realms/ACBrAPI/protocol/openid-connect/token\",\n  \"empresa conta cnpj cep nfce nfe\"\n);\n```\n\n---\n\n## Usando o Client com o Token\n\n### Com `fetch` (nativo Node 18+)\n\n```ts\nimport {\n  AcbrApi,\n  FetchHttpClient,\n  getAccessTokenString,\n} from \"@alijunior/acbr-api-sdk-node\";\n\nconst accessToken = await getAccessTokenString({\n  tokenUrl: process.env.TOKEN_URL!,\n  clientId: process.env.CLIENT_ID!,\n  clientSecret: process.env.CLIENT_SECRET!,\n  scope: process.env.SCOPE,\n});\n\nconst http = new FetchHttpClient({\n  headers: { Authorization: `Bearer ${accessToken}` },\n});\n\nconst api = new AcbrApi(http, process.env.API_BASE!);\n\n// CEP\nconst cep = await api.consultarCep({ Cep: \"04513010\" });\nconsole.log(\"CEP:\", cep);\n\n// CNPJ (exemplo)\nconst cnpjList = await api.listarCnpj({\n  cnae_principal: \"6201501\",\n  municipio: \"3550308\",\n  natureza_juridica: \"2062\",\n  $top: 5,\n  $skip: 0,\n  $inlinecount: false,\n});\nconsole.log(\"CNPJ:\", cnpjList);\n```\n\n### Com `axios` (opcional)\n\nO `AxiosHttpClient` recebe um objeto de configuração. Você pode passar apenas o `accessToken` (o adapter cria a instância axios internamente) **ou** fornecer uma `axiosInstance` própria.\n\n```ts\nimport {\n  AcbrApi,\n  AxiosHttpClient,\n  getAccessTokenString,\n} from \"@alijunior/acbr-api-sdk-node\";\n\nconst accessToken = await getAccessTokenString({\n  tokenUrl: process.env.TOKEN_URL!,\n  clientId: process.env.CLIENT_ID!,\n  clientSecret: process.env.CLIENT_SECRET!,\n});\n\nconst http = new AxiosHttpClient({\n  accessToken,\n  baseURL: process.env.API_BASE!,\n});\n\nconst api = new AcbrApi(http, process.env.API_BASE!);\n\n// exemplo\nconst cep = await api.consultarCep({ Cep: \"01001000\" });\n```\n\nCaso prefira controlar a instância axios (interceptors, timeout, etc.), passe-a via `axiosInstance`:\n\n```ts\nimport axios from \"axios\";\nimport { AxiosHttpClient } from \"@alijunior/acbr-api-sdk-node\";\n\nconst axiosInstance = axios.create({\n  baseURL: process.env.API_BASE!,\n  headers: { Authorization: `Bearer ${accessToken}` },\n});\n\nconst http = new AxiosHttpClient({ axiosInstance });\n```\n\n> **Opções do construtor do `AxiosHttpClient`:** `{ accessToken?, axiosInstance?, baseURL?, headers?, timeoutMs? }`. Se `accessToken` e `axiosInstance` forem informados simultaneamente, a `axiosInstance` prevalece.\n\n---\n\n## Exemplo completo: empresa, certificado e emissão\n\nFluxo típico: cadastrar a empresa emitente, enviar o certificado digital, configurar a NFC-e e emitir.\n\n```ts\nimport {\n  AcbrApi,\n  FetchHttpClient,\n  getAccessTokenString,\n  type Empresa,\n} from \"@alijunior/acbr-api-sdk-node\";\n\nconst accessToken = await getAccessTokenString({\n  tokenUrl: process.env.TOKEN_URL!,\n  clientId: process.env.CLIENT_ID!,\n  clientSecret: process.env.CLIENT_SECRET!,\n  scope: process.env.SCOPE,\n});\n\nconst http = new FetchHttpClient({\n  headers: { Authorization: `Bearer ${accessToken}` },\n});\nconst api = new AcbrApi(http, process.env.API_BASE!);\n\n// 1) Cadastrar a empresa emitente\nconst empresa: Empresa = {\n  cpf_cnpj: \"00000000000000\", // sem máscara\n  nome_razao_social: \"Minha Empresa LTDA\",\n  nome_fantasia: \"Minha Empresa\",\n  email: \"contato@minhaempresa.com.br\",\n  inscricao_estadual: \"ISENTO\",\n  endereco: {\n    logradouro: \"Rua Exemplo\",\n    numero: \"100\",\n    bairro: \"Centro\",\n    codigo_municipio: \"3550308\", // código IBGE\n    uf: \"SP\",\n    cep: \"01001000\", // sem máscara\n  },\n};\n\n// criarEmpresa cria ou, se já existir, normalmente retorna conflito —\n// trate o erro e use atualizarEmpresa quando necessário (veja Tratamento de erros).\nawait api.criarEmpresa(empresa);\n\n// 2) Cadastrar o certificado digital (.pfx/.p12 em base64)\nawait api.cadastrarCertificadoEmpresa(\n  { cpf_cnpj: empresa.cpf_cnpj },\n  {\n    certificado: \"BASE64_DO_PFX\",\n    password: \"senha-do-certificado\",\n  }\n);\n\n// 3) Configurar a NFC-e (CRT + CSC da SEFAZ)\n//    CRT: 1=Simples Nacional, 2=SN excesso de sublimite, 3=Regime Normal, 4=MEI.\nawait api.alterarConfigNfce(\n  { cpf_cnpj: empresa.cpf_cnpj },\n  {\n    CRT: 1,\n    ambiente: \"homologacao\", // ou \"producao\"\n    sefaz: {\n      id_csc: 1,\n      csc: \"CODIGO_CSC_DA_SEFAZ\",\n    },\n  }\n);\n\n// 4) Consultar a empresa\nconst cadastrada = await api.consultarEmpresa({ cpf_cnpj: empresa.cpf_cnpj });\nconsole.log(\"Empresa cadastrada:\", cadastrada.nome_razao_social);\n\n// 5) Emitir uma NFC-e (monte o pedido conforme o tipo NfePedidoEmissao)\n// const nfce = await api.emitirNfce(pedido);\n// console.log(\"Status:\", nfce.status, \"Chave:\", nfce.chave);\n```\n\n> Operações de exclusão (ex.: `excluirEmpresa`, `excluirCertificadoEmpresa`) retornam `void`: a API responde `204 No Content` e o adapter resolve sem corpo.\n\n---\n\n## Tratamento de erros (exemplo)\n\nOs adapters levantam `Error` com `status` e, se presente, `body`.\n\n```ts\nfunction printHttpError(e: unknown, ctx?: string) {\n  const status = (e as { status?: number }).status;\n  const body = (e as { body?: { error?: { message?: string }; message?: string } }).body;\n  const msg =\n    body?.error?.message ??\n    body?.message ??\n    (e as Error)?.message ??\n    String(e);\n\n  console.error(\n    `${ctx ? ctx + \": \" : \"\"}${status ? status + \" - \" : \"\"}${msg}`\n  );\n}\n\ntry {\n  const res = await api.listarCnpj({\n    /* ... */\n  });\n} catch (e) {\n  printHttpError(e, \"Falha ao listar CNPJ\");\n}\n```\n\n> Em adapters baseados em `fetch`/`axios` deste SDK, o erro expõe `status` e `body` diretamente (ex.: `err.status`), e **não** `err.response.status` (formato do axios cru). Trate uma consulta `404` como \"registro não encontrado\".\n\n> **Empresa já cadastrada:** ao tentar `criarEmpresa` para um CNPJ existente, a Acbr API pode retornar um erro com código do tipo `EmpresaAlreadyExists`. Nesse caso, faça o fallback para `atualizarEmpresa`. A detecção mais segura é verificar `status === 409` **ou** `/AlreadyExists/i.test(code)`, já que o status retornado pode variar.\n\n---\n\n## Tipos úteis (reexportados)\n\n```ts\nimport type {\n  CepEndereco,\n  CnpjListagem,\n  CnpjEmpresa,\n  ContaCotaListagem,\n  ContaCota,\n  EmpresaListagem,\n  Empresa,\n  EmpresaCertificado,\n  NfePedidoEmissao,\n  Dfe,\n  NfePedidoCancelamento,\n  DfeCancelamento,\n  EmpresaPedidoCadastroCertificado,\n} from \"@alijunior/acbr-api-sdk-node\";\n```\n\n> Os tipos de **params/query/body/response** dos métodos são inferidos das **operations**/**paths** do OpenAPI. Se o contrato mudar, o TypeScript aponta onde ajustar.\n\n---\n\n## Download de binários\n\nMétodos como `baixarPdfNfe` retornam `ArrayBuffer`.\n\n```typescript\n// Node: salvar em disco\nimport { writeFile } from \"node:fs/promises\";\n\nconst pdf = await api.baixarPdfNfe({ id: \"...\" });\nawait writeFile(\"nota.pdf\", Buffer.from(pdf));\n```\n\n```typescript\n// Browser: forçar download\n\nconst ab = await api.baixarPdfNfe({ id: \"...\" });\nconst blob = new Blob([ab], { type: \"application/pdf\" });\nconst url = URL.createObjectURL(blob);\nconst a = document.createElement(\"a\");\na.href = url;\na.download = \"nota.pdf\";\na.click();\nURL.revokeObjectURL(url);\n```\n\n---\n\n## Uso com assistentes de IA (`llms.txt`)\n\nO pacote inclui um arquivo [`llms.txt`](./llms.txt) na raiz, seguindo a convenção [llmstxt.org](https://llmstxt.org/). Ele descreve, em formato otimizado para LLMs, as assinaturas verificadas dos helpers de token e adapters, os fluxos de uso em **backend** e **browser** (incluindo a restrição de CORS do Keycloak), e as armadilhas mais comuns.\n\nAo gerar código que usa este SDK com um assistente, forneça o conteúdo de `llms.txt` como contexto para obter exemplos corretos.\n\n---\n\n## Desenvolvimento\n\n```bash\n# exemplos locais (usando .env)\npnpm test\n\n# build da lib\npnpm build\n```\n\n---\n\n## Segurança\n\n- **Nunca** exponha `CLIENT_SECRET` no front em produção.\n- Faça a emissão de token **no backend** e envie apenas o `access_token` ao cliente.\n- Rotacione/Revogue credenciais comprometidas.\n\n---\n\n## Licença\n\n[MIT](./LICENSE)","readmeFilename":"README.md"}