@sevn/reqcache 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -221,15 +221,23 @@ meio da fila:
221
221
 
222
222
  | Rede estimada | Timeout por tentativa |
223
223
  | ------------- | --------------------- |
224
- | `4g` | 30 s |
225
- | `3g` | 45 s |
226
- | `2g` | 60 s |
227
- | `slow-2g` | 90 s |
228
- | desconhecida | 30 s |
224
+ | `4g` | 8 s |
225
+ | `3g` | 12 s |
226
+ | `2g` | 16 s |
227
+ | `slow-2g` | 24 s |
228
+ | desconhecida | 8 s |
229
229
 
230
230
  `desconhecida` é o que vale em SSR e nos browsers sem a API — **Safari e
231
231
  Firefox não a implementam**, então boa parte do tráfego real cai no tier de
232
- 30 s independentemente da rede de verdade.
232
+ 8 s independentemente da rede de verdade.
233
+
234
+ A base é dimensionada para uma tela que repete a chamada a cada ~10 s: uma
235
+ resposta que chega depois do próximo tick já nasce velha, então abortar antes
236
+ dele deixa o ciclo seguinte começar limpo — e o `staleOnError` devolve o dado
237
+ anterior em vez de travar a UI. Os tiers piores passam de um ciclo de
238
+ propósito: a deduplicação por rota faz o polling desacelerar sozinho quando a
239
+ rede não dá conta. Se o seu caso é uma chamada pontual com payload grande, o
240
+ ajuste certo é `timeoutMs` na chamada, não inflar o tier para todo mundo.
233
241
 
234
242
  O `effectiveType` é uma estimativa do browser a partir de latência e throughput
235
243
  recentes, não o rádio do aparelho: um 4g congestionado é reportado como `2g` —
package/dist/index.d.ts CHANGED
@@ -253,14 +253,23 @@ declare class RequestCache {
253
253
  /**
254
254
  * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.
255
255
  *
256
- * 4g / desconhecida 30 s — base
257
- * 3g 45 s — 1,5x
258
- * 2g 60 s — 2x
259
- * slow-2g 90 s — 3x; borda de cobertura, interior
256
+ * 4g / desconhecida 8 s — base
257
+ * 3g 12 s — 1,5x
258
+ * 2g 16 s — 2x
259
+ * slow-2g 24 s — 3x; borda de cobertura, interior
260
260
  *
261
- * A folga cresce com a rede ruim para não matar uma resposta que ainda viria
262
- * num client lento. O custo aparece em `fallbackUrls`, porque a fila é
263
- * sequencial: o pior caso é N x o tier veja `timeoutMs` em GetFetchOptions.
261
+ * A base fica logo abaixo de um ciclo de polling de 10 s: numa tela que
262
+ * repete a chamada, a resposta que chega depois do próximo tick já nasce
263
+ * velha o tick novo traz dado mais fresco. Abortar a tempo deixa o ciclo
264
+ * seguinte começar limpo, e o `staleOnError` cobre o buraco com o dado
265
+ * anterior em vez de travar a UI.
266
+ *
267
+ * A folga ainda cresce com a rede ruim, e os tiers piores passam de um ciclo
268
+ * de propósito: lá a deduplicação por rota faz o polling desacelerar sozinho,
269
+ * o que é melhor do que martelar um client que não dá conta.
270
+ *
271
+ * O custo aparece em `fallbackUrls`, porque a fila é sequencial: o pior caso
272
+ * é N x o tier — veja `timeoutMs` em GetFetchOptions.
264
273
  */
265
274
  declare const TIMEOUTS_POR_REDE: Record<EffectiveConnectionType | "desconhecida", number>;
266
275
  /** Instância pronta para uso, caso não queira configurar nada. */
package/dist/index.esm.js CHANGED
@@ -491,21 +491,30 @@ const STATUS_SEM_FALLBACK_PADRAO = [404];
491
491
  /**
492
492
  * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.
493
493
  *
494
- * 4g / desconhecida 30 s — base
495
- * 3g 45 s — 1,5x
496
- * 2g 60 s — 2x
497
- * slow-2g 90 s — 3x; borda de cobertura, interior
494
+ * 4g / desconhecida 8 s — base
495
+ * 3g 12 s — 1,5x
496
+ * 2g 16 s — 2x
497
+ * slow-2g 24 s — 3x; borda de cobertura, interior
498
498
  *
499
- * A folga cresce com a rede ruim para não matar uma resposta que ainda viria
500
- * num client lento. O custo aparece em `fallbackUrls`, porque a fila é
501
- * sequencial: o pior caso é N x o tier veja `timeoutMs` em GetFetchOptions.
499
+ * A base fica logo abaixo de um ciclo de polling de 10 s: numa tela que
500
+ * repete a chamada, a resposta que chega depois do próximo tick já nasce
501
+ * velha o tick novo traz dado mais fresco. Abortar a tempo deixa o ciclo
502
+ * seguinte começar limpo, e o `staleOnError` cobre o buraco com o dado
503
+ * anterior em vez de travar a UI.
504
+ *
505
+ * A folga ainda cresce com a rede ruim, e os tiers piores passam de um ciclo
506
+ * de propósito: lá a deduplicação por rota faz o polling desacelerar sozinho,
507
+ * o que é melhor do que martelar um client que não dá conta.
508
+ *
509
+ * O custo aparece em `fallbackUrls`, porque a fila é sequencial: o pior caso
510
+ * é N x o tier — veja `timeoutMs` em GetFetchOptions.
502
511
  */
503
512
  const TIMEOUTS_POR_REDE = {
504
- "slow-2g": 90000,
505
- "2g": 60000,
506
- "3g": 45000,
507
- "4g": 30000,
508
- desconhecida: 30000,
513
+ "slow-2g": 24000,
514
+ "2g": 16000,
515
+ "3g": 12000,
516
+ "4g": 8000,
517
+ desconhecida: 8000,
509
518
  };
510
519
  const REDES_CONHECIDAS = ["slow-2g", "2g", "3g", "4g"];
511
520
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/request-cache.ts"],"sourcesContent":["/**\r\n * request-cache\r\n * -------------\r\n * Cache de requisições HTTP em localStorage, pensado para cenários de\r\n * alto volume em curto período (ex.: apuração de eleições).\r\n *\r\n * Modelo de armazenamento:\r\n * Tudo fica sob UMA chave no storage, contendo um ARRAY de objetos.\r\n * Cada objeto é indexado pela `rota` (a URL requisitada):\r\n * [{ rota, data, createdAt, expiresAt, lastAccess }, ...]\r\n *\r\n * Comportamento:\r\n * 1. Primeira chamada -> faz o fetch e grava a entrada da rota no array.\r\n * 2. Durante o TTL -> devolve os dados do cache, sem tocar na rede.\r\n * 3. Após expirar -> SÓ revalida quando o client chamar de novo (lazy).\r\n * Se houver `monotonicKey`, só aceita o novo dado quando TODAS as chaves\r\n * tiverem AVANÇADO; senão mantém o antigo.\r\n *\r\n * Proteção de espaço (limite de ~5 MB do localStorage):\r\n * - `maxEntries`: ao gravar, se passar do limite, remove a rota menos\r\n * recentemente usada (LRU). Roda sozinho, sem timer.\r\n * - Em erro de quota, remove as entradas mais antigas e tenta de novo.\r\n * - `cleanup()`: remove rotas expiradas há mais de `graceSeconds`.\r\n *\r\n * Extras para o caso de eleição:\r\n * - Deduplicação: chamadas simultâneas à mesma rota disparam 1 só fetch.\r\n * - staleOnError: se a rede/API falhar, devolve o último dado válido.\r\n */\r\n\r\nexport interface StorageLike {\r\n getItem(key: string): string | null;\r\n setItem(key: string, value: string): void;\r\n removeItem(key: string): void;\r\n}\r\n\r\n/**\r\n * Como comparar o valor de uma chave monotônica:\r\n * - `\"number\"` — numérica. Aceita `number` e string numérica (\"2232575810\").\r\n * - `\"date\"` — por timestamp. Aceita o que o `Date.parse` resolver\r\n * (\"2026-10-04T18:23:00Z\") e também epoch em ms.\r\n * - `\"string\"` — lexicográfica.\r\n */\r\nexport type MonotonicType = \"number\" | \"date\" | \"string\";\r\n\r\n/**\r\n * Chave(s) monotônica(s). Três formas:\r\n * - `\"summary.ballots_counted\"` — uma chave, tipo detectado pelo valor.\r\n * - `[\"idg\", \"summary.last_updated\"]` — várias, tipo detectado pelo valor.\r\n * - `{ idg: \"number\", \"summary.last_updated\": \"date\" }` — várias, com o tipo\r\n * declarado por nome de chave (recomendado: não depende de adivinhação).\r\n */\r\nexport type MonotonicKey = string | string[] | Record<string, MonotonicType>;\r\n\r\n/**\r\n * Qualidade de rede estimada pelo browser (Network Information API).\r\n * É uma ESTIMATIVA baseada em latência e throughput recentes, não o rádio do\r\n * aparelho: um 4g congestionado é reportado como \"2g\" — que é exatamente o\r\n * caso que interessa aqui.\r\n */\r\nexport type EffectiveConnectionType = \"slow-2g\" | \"2g\" | \"3g\" | \"4g\";\r\n\r\n/**\r\n * Timeout de UMA tentativa de fetch, em ms, por qualidade de rede.\r\n * `desconhecida` é o que vale em SSR e nos browsers sem a Network Information\r\n * API (Safari e Firefox).\r\n */\r\nexport type TimeoutTiers = Partial<\r\n Record<EffectiveConnectionType | \"desconhecida\", number>\r\n>;\r\n\r\n/**\r\n * Falha de HTTP com o status preservado. É o que permite decidir se vale a\r\n * pena tentar o próximo domínio (503, sim) ou não (404).\r\n */\r\nexport class HttpError extends Error {\r\n constructor(\r\n readonly status: number,\r\n readonly url: string,\r\n ) {\r\n super(`HTTP ${status} ao buscar ${url}`);\r\n this.name = \"HttpError\";\r\n }\r\n}\r\n\r\nexport interface GetFetchOptions {\r\n /**\r\n * Caminho(s) da chave monotônica. Suporta aninhamento com ponto.\r\n *\r\n * monotonicKey: \"resultado.votosApurados\"\r\n * monotonicKey: [\"idg\", \"summary.last_updated\"]\r\n * monotonicKey: { idg: \"number\", \"summary.last_updated\": \"date\" }\r\n *\r\n * Com mais de uma chave, TODAS são comparadas e o dado novo só é aceito\r\n * quando todas concordam que ele é mais recente — se qualquer uma vier\r\n * igual ou menor, a resposta é descartada e o dado em cache é mantido.\r\n * A comparação é sempre estrita: o valor novo precisa ser MAIOR que o\r\n * guardado, nunca igual.\r\n *\r\n * O tipo de cada chave é resolvido nesta ordem:\r\n * 1. declarado na forma de mapa;\r\n * 2. conhecido pelo NOME da chave (`idg` é número, `last_updated` é data —\r\n * veja `TIPOS_POR_NOME`, extensível via `monotonicTypes` na config);\r\n * 3. detectado pelo valor: número, string que converte para número finito,\r\n * string que o `Date.parse` resolve e, por fim, lexicográfica.\r\n *\r\n * TODAS as chaves precisam ter crescido. Uma chave incomparável (ausente de\r\n * um dos lados, ilegível ou fora do tipo esperado) conta como \"não cresceu\"\r\n * e derruba a resposta nova junto com as demais.\r\n */\r\n monotonicKey?: MonotonicKey;\r\n\r\n /** Opções nativas repassadas ao fetch (headers, method, body, signal...). */\r\n fetchOptions?: RequestInit;\r\n\r\n /**\r\n * Se true (padrão), quando a nova requisição falhar e existir dado antigo,\r\n * devolve esse dado antigo em vez de lançar erro.\r\n */\r\n staleOnError?: boolean;\r\n\r\n /** fetch customizado — útil para testes ou ambientes sem fetch global. */\r\n fetcher?: typeof fetch;\r\n\r\n /**\r\n * URLs alternativas (outros domínios/APIs) tentadas em ordem, caso `url`\r\n * falhe. A rota do cache continua sendo `url` — as alternativas só entram\r\n * na requisição, não criam entradas novas no cache.\r\n */\r\n fallbackUrls?: string[];\r\n\r\n /**\r\n * Chamado quando uma URL falha e a lib vai tentar a próxima da lista\r\n * (`url` + `fallbackUrls`). Útil para observabilidade/log.\r\n */\r\n onFallback?: (failedUrl: string, error: unknown) => void;\r\n\r\n /**\r\n * Timeout de CADA tentativa, em ms. Quando omitido, sai da tabela por rede\r\n * (`timeoutTiers` na config). Vale por candidato, não pela fila toda: com\r\n * dois `fallbackUrls`, o pior caso é 3x esse valor.\r\n */\r\n timeoutMs?: number;\r\n\r\n /**\r\n * Status HTTP que NÃO devem acionar o fallback. Padrão: `[404]`.\r\n *\r\n * Um 404 é uma resposta correta do servidor — \"esse recurso não existe\" —,\r\n * não uma indisponibilidade. Repetir o mesmo caminho em outro domínio tende\r\n * a devolver o mesmo 404, só que N vezes mais devagar, então o erro sobe na\r\n * hora. Passe `[]` para voltar ao comportamento antigo (tentar todos).\r\n */\r\n statusSemFallback?: number[];\r\n}\r\n\r\n/** Uma entrada do cache. `rota` é a chave identificadora. */\r\nexport interface CacheEntry<T = unknown> {\r\n rota: string;\r\n data: T;\r\n createdAt: number;\r\n expiresAt: number;\r\n lastAccess: number;\r\n}\r\n\r\n/** Uma linha de log do modo debug. */\r\nexport interface DebugLogFn {\r\n (message: string, details?: Record<string, unknown>): void;\r\n}\r\n\r\nexport interface RequestCacheConfig {\r\n /** Storage a usar. Padrão: localStorage (se disponível). */\r\n storage?: StorageLike;\r\n /** Chave única onde o array é guardado. Padrão \"reqcache\". */\r\n storageKey?: string;\r\n /** Máximo de rotas em cache. Excedeu -> remove a menos usada (LRU). Padrão 100. */\r\n maxEntries?: number;\r\n\r\n /**\r\n * Se true, imprime logs detalhados de cada operação (fetch de rede,\r\n * cache hit, deduplicação, fallback, LRU, etc.). Padrão false.\r\n * Útil para identificar se estão sendo feitas mais chamadas de rede\r\n * do que o necessário. Pode ser ligado/desligado em runtime com\r\n * `setDebug()`.\r\n */\r\n debug?: boolean;\r\n\r\n /** Logger customizado usado quando `debug` está ligado. Padrão: `console.debug`. */\r\n logger?: DebugLogFn;\r\n\r\n /**\r\n * Tipos monotônicos adicionais, por nome de chave. Somado (e com prioridade\r\n * sobre) a tabela embutida `TIPOS_POR_NOME`, para que o chamador possa\r\n * continuar passando `monotonicKey` como lista de caminhos e ainda assim ter\r\n * a comparação certa:\r\n *\r\n * new RequestCache({ monotonicTypes: { data_apuracao: \"date\" } })\r\n * ...\r\n * getFetch(url, 10_000, { monotonicKey: [\"idg\", \"data_apuracao\"] })\r\n *\r\n * Aceita o caminho completo (\"summary.last_updated\") ou só o último trecho\r\n * (\"last_updated\").\r\n */\r\n monotonicTypes?: Record<string, MonotonicType>;\r\n\r\n /**\r\n * Timeout de cada tentativa de fetch, por qualidade de rede do client.\r\n * O que for passado aqui é mesclado sobre `TIMEOUTS_POR_REDE`:\r\n *\r\n * new RequestCache({ timeoutTiers: { \"slow-2g\": 120_000 } })\r\n *\r\n * Um `timeoutMs` na chamada tem prioridade sobre esta tabela.\r\n */\r\n timeoutTiers?: TimeoutTiers;\r\n}\r\n\r\nexport class RequestCache {\r\n private storage: StorageLike | null;\r\n private storageKey: string;\r\n private maxEntries: number;\r\n private inflight = new Map<string, Promise<unknown>>();\r\n /** Relógio de acesso monotônico: sempre cresce, mesmo com acessos no mesmo ms. */\r\n private tick = 0;\r\n private debug: boolean;\r\n private logger: DebugLogFn;\r\n private monotonicTypes: Record<string, MonotonicType>;\r\n private timeoutTiers: Record<EffectiveConnectionType | \"desconhecida\", number>;\r\n\r\n constructor(config: RequestCacheConfig = {}) {\r\n this.storageKey = config.storageKey ?? \"reqcache\";\r\n this.maxEntries = config.maxEntries ?? 100;\r\n this.storage = config.storage ?? getDefaultStorage();\r\n this.debug = config.debug ?? false;\r\n this.logger = config.logger ?? defaultLogger;\r\n this.monotonicTypes = { ...TIPOS_POR_NOME, ...(config.monotonicTypes ?? {}) };\r\n this.timeoutTiers = { ...TIMEOUTS_POR_REDE, ...(config.timeoutTiers ?? {}) };\r\n }\r\n\r\n /** Liga/desliga o modo debug em runtime, sem precisar recriar a instância. */\r\n setDebug(enabled: boolean): void {\r\n this.debug = enabled;\r\n }\r\n\r\n /**\r\n * Busca uma URL usando cache.\r\n * @param url Rota da requisição (também é a chave do cache).\r\n * @param ttlMs Tempo de cache em milissegundos (ex.: 10000).\r\n * @param options Regra monotônica, opções de fetch, etc.\r\n */\r\n async getFetch<T = unknown>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions = {},\r\n ): Promise<T> {\r\n const now = Date.now();\r\n const all = this.readAll();\r\n const cached = all.find((e) => e.rota === url) as CacheEntry<T> | undefined;\r\n\r\n // 1. Cache ainda válido -> devolve sem tocar na rede.\r\n if (cached && now < cached.expiresAt) {\r\n cached.lastAccess = this.nextAccess(); // marca uso p/ o LRU\r\n this.writeAll(all);\r\n this.log(\"cache hit\", { url, expiresAt: new Date(cached.expiresAt).toISOString() });\r\n return cached.data;\r\n }\r\n\r\n // 2. Deduplicação: se já há uma requisição em andamento p/ essa rota,\r\n // todas as chamadas concorrentes aguardam a mesma Promise.\r\n const pending = this.inflight.get(url) as Promise<T> | undefined;\r\n if (pending) {\r\n this.log(\"dedup: aguardando requisição em andamento\", { url });\r\n return pending;\r\n }\r\n\r\n this.log(cached ? \"cache expirado, revalidando\" : \"cache miss\", { url });\r\n\r\n const promise = this.revalidate<T>(url, ttlMs, options, cached ?? null)\r\n .finally(() => this.inflight.delete(url));\r\n\r\n this.inflight.set(url, promise);\r\n return promise;\r\n }\r\n\r\n /** Remove uma rota específica do cache. */\r\n invalidate(url: string): void {\r\n const all = this.readAll().filter((e) => e.rota !== url);\r\n this.writeAll(all);\r\n }\r\n\r\n /**\r\n * Remove rotas expiradas há mais de `graceSeconds` (padrão 3600 = 1h).\r\n * Chame na inicialização do app, ou de tempos em tempos.\r\n * @returns quantidade de rotas removidas.\r\n */\r\n cleanup(graceSeconds = 3600): number {\r\n const limite = Date.now() - graceSeconds * 1000;\r\n const all = this.readAll();\r\n const mantidas = all.filter((e) => e.expiresAt > limite);\r\n this.writeAll(mantidas);\r\n const removidas = all.length - mantidas.length;\r\n if (removidas > 0) this.log(\"cleanup: rotas expiradas removidas\", { removidas });\r\n return removidas;\r\n }\r\n\r\n /** Esvazia todo o cache. */\r\n clear(): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.removeItem(this.storageKey);\r\n } catch {\r\n /* storage indisponível */\r\n }\r\n }\r\n\r\n // --- interno ------------------------------------------------------------\r\n\r\n /** Retorna um número de acesso estritamente crescente (para o LRU). */\r\n private nextAccess(): number {\r\n this.tick = Math.max(Date.now(), this.tick + 1);\r\n return this.tick;\r\n }\r\n\r\n /** Emite uma linha de log, só quando o modo debug está ligado. */\r\n private log(message: string, details?: Record<string, unknown>): void {\r\n if (!this.debug) return;\r\n this.logger(message, details);\r\n }\r\n\r\n /**\r\n * Timeout de UMA tentativa, em ms. A ordem de prioridade é:\r\n * 1. `timeoutMs` passado na chamada;\r\n * 2. o tier da rede atual do client (`navigator.connection`);\r\n * 3. o tier `desconhecida`, usado em SSR, Safari e Firefox.\r\n *\r\n * A rede é relida a cada tentativa de propósito: numa apuração o client pode\r\n * sair do wi-fi e cair no 3g no meio da fila de fallback.\r\n */\r\n private resolverTimeout(options: GetFetchOptions): number {\r\n if (typeof options.timeoutMs === \"number\") return options.timeoutMs;\r\n return this.timeoutTiers[detectarRede() ?? \"desconhecida\"];\r\n }\r\n\r\n /**\r\n * Tenta cada URL da lista em ordem (primário, depois os `fallbackUrls`).\r\n * Devolve o JSON da primeira que responder OK; se todas falharem, lança o\r\n * último erro.\r\n *\r\n * Duas situações encerram a fila antes do fim, em vez de seguir para o\r\n * próximo candidato:\r\n * - um status listado em `statusSemFallback` (padrão: 404). É resposta do\r\n * servidor, não indisponibilidade — o outro domínio diria o mesmo.\r\n * - o `signal` do chamador abortado. Foi cancelamento de quem pediu;\r\n * insistir só trocaria o erro real pelo AbortError do último candidato.\r\n */\r\n private async fetchComRedundancia<T>(\r\n candidatos: string[],\r\n fetcher: typeof fetch,\r\n options: GetFetchOptions,\r\n ): Promise<T> {\r\n const semFallback = new Set(\r\n options.statusSemFallback ?? STATUS_SEM_FALLBACK_PADRAO,\r\n );\r\n const externo = options.fetchOptions?.signal ?? null;\r\n let ultimoErro: unknown;\r\n\r\n for (const candidato of candidatos) {\r\n if (externo?.aborted) {\r\n this.log(\"cancelado pelo chamador: fila interrompida\", { url: candidato });\r\n throw externo.reason ?? new Error(\"Requisição cancelada pelo chamador\");\r\n }\r\n\r\n const timeoutMs = this.resolverTimeout(options);\r\n const tentativa = criarSinal(timeoutMs, externo);\r\n this.log(\"fetch de rede\", { url: candidato, timeoutMs, rede: detectarRede() });\r\n\r\n try {\r\n const res = await fetcher(candidato, {\r\n ...options.fetchOptions,\r\n signal: tentativa.signal,\r\n });\r\n if (!res.ok) throw new HttpError(res.status, candidato);\r\n const json = (await res.json()) as T;\r\n this.log(\"fetch OK\", { url: candidato, status: res.status });\r\n return json;\r\n } catch (bruto) {\r\n // Um abort disparado pelo nosso timer chega aqui como AbortError\r\n // genérico; troca por um erro que diz o que de fato aconteceu.\r\n const err = tentativa.estourou()\r\n ? erroDeTimeout(candidato, timeoutMs)\r\n : bruto;\r\n\r\n this.log(\"fetch falhou\", { url: candidato, erro: String(err) });\r\n ultimoErro = err;\r\n options.onFallback?.(candidato, err);\r\n\r\n if (err instanceof HttpError && semFallback.has(err.status)) {\r\n this.log(\"status sem fallback: fila interrompida\", {\r\n url: candidato,\r\n status: err.status,\r\n });\r\n throw err;\r\n }\r\n } finally {\r\n // Obrigatório: sem isso sobra um timer de minutos pendurado por\r\n // tentativa, segurando o event loop no Node e vazando no browser.\r\n tentativa.cancelar();\r\n }\r\n }\r\n\r\n throw ultimoErro;\r\n }\r\n\r\n private async revalidate<T>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions,\r\n cached: CacheEntry<T> | null,\r\n ): Promise<T> {\r\n const fetcher = options.fetcher ?? fetch;\r\n const candidatos = [url, ...(options.fallbackUrls ?? [])];\r\n let fresh: T;\r\n\r\n try {\r\n fresh = await this.fetchComRedundancia<T>(candidatos, fetcher, options);\r\n } catch (err) {\r\n // Todos os domínios falharam. Se temos dado antigo e staleOnError, devolve o antigo.\r\n if (cached && (options.staleOnError ?? true)) {\r\n this.log(\"staleOnError: devolvendo dado antigo\", { url });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n throw err;\r\n }\r\n\r\n // 3. Regra monotônica: só aceita o novo dado se TODAS as chaves que\r\n // conseguiram votar tiverem avançado.\r\n if (cached && options.monotonicKey) {\r\n const veredito = avaliarMonotonicidade(\r\n cached.data,\r\n fresh,\r\n options.monotonicKey,\r\n this.monotonicTypes,\r\n );\r\n\r\n if (!veredito.avancou) {\r\n // Alguma chave não avançou -> mantém o dado antigo, só renova a expiração.\r\n this.log(\"regra monotônica: valor não cresceu, mantendo dado antigo\", {\r\n url,\r\n chaves: veredito.detalhes,\r\n });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n }\r\n\r\n // 4. Grava e devolve o dado novo.\r\n const now = Date.now();\r\n this.log(\"cache gravado\", { url, ttlMs });\r\n this.upsert<T>({\r\n rota: url,\r\n data: fresh,\r\n createdAt: now,\r\n expiresAt: now + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return fresh;\r\n }\r\n\r\n /** Insere ou atualiza uma rota, aplicando o limite LRU. */\r\n private upsert<T>(entry: CacheEntry<T>): void {\r\n const all = this.readAll().filter((e) => e.rota !== entry.rota);\r\n all.push(entry);\r\n this.evictIfNeeded(all);\r\n this.writeAll(all);\r\n }\r\n\r\n /** Enquanto passar de maxEntries, remove a rota menos recentemente usada. */\r\n private evictIfNeeded(all: CacheEntry[]): void {\r\n while (all.length > this.maxEntries) {\r\n let idxMaisAntigo = 0;\r\n for (let i = 1; i < all.length; i++) {\r\n if (all[i].lastAccess < all[idxMaisAntigo].lastAccess) idxMaisAntigo = i;\r\n }\r\n this.log(\"LRU: removendo rota menos usada\", { url: all[idxMaisAntigo].rota });\r\n all.splice(idxMaisAntigo, 1);\r\n }\r\n }\r\n\r\n private readAll(): CacheEntry[] {\r\n if (!this.storage) return [];\r\n try {\r\n const raw = this.storage.getItem(this.storageKey);\r\n if (!raw) return [];\r\n const parsed = JSON.parse(raw);\r\n return Array.isArray(parsed) ? (parsed as CacheEntry[]) : [];\r\n } catch {\r\n return []; // JSON corrompido -> trata como cache vazio\r\n }\r\n }\r\n\r\n private writeAll(all: CacheEntry[]): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(all));\r\n } catch (err) {\r\n // Quota estourada: remove os mais antigos e tenta de novo.\r\n if (isQuotaError(err) && all.length > 0) {\r\n const reduzido = [...all].sort((a, b) => a.lastAccess - b.lastAccess);\r\n reduzido.splice(0, Math.ceil(reduzido.length / 2)); // descarta metade\r\n this.log(\"quota excedida: descartando metade das entradas mais antigas\", {\r\n totalAntes: all.length,\r\n totalDepois: reduzido.length,\r\n });\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(reduzido));\r\n } catch {\r\n /* ainda assim falhou -> desiste de persistir; dados já retornam */\r\n }\r\n }\r\n // Falha silenciosa: os dados ainda são devolvidos ao chamador.\r\n }\r\n }\r\n}\r\n\r\n/** Lê um caminho aninhado (\"a.b.c\") de um objeto de forma segura. */\r\nfunction getPath(obj: unknown, path: string): unknown {\r\n return path.split(\".\").reduce<unknown>((acc, k) => {\r\n if (acc && typeof acc === \"object\" && k in (acc as object)) {\r\n return (acc as Record<string, unknown>)[k];\r\n }\r\n return undefined;\r\n }, obj);\r\n}\r\n\r\n/** Como um valor deve ser comparado, decidido pelo próprio valor. */\r\ntype TipoComparacao = \"number\" | \"date\" | \"string\";\r\n\r\n/** Um valor já classificado, pronto para ser comparado com outro do mesmo tipo. */\r\ninterface ValorComparavel {\r\n tipo: TipoComparacao;\r\n ordem: number | string;\r\n}\r\n\r\n/**\r\n * Descobre como comparar um valor. A ordem das checagens NÃO pode ser trocada:\r\n * `Date.parse` aceita strings numéricas curtas como data (`Date.parse(\"9\")` cai\r\n * em setembro, `Date.parse(\"10\")` em outubro), então um `idg` curto viraria\r\n * data se a checagem de data viesse antes da numérica. No sentido inverso não\r\n * há risco: `Number(\"2026-10-04T18:23:00Z\")` é NaN.\r\n *\r\n * Devolve `null` quando o valor é incomparável.\r\n */\r\nfunction classificar(valor: unknown): ValorComparavel | null {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo: \"number\", ordem: valor } : null;\r\n }\r\n if (typeof valor !== \"string\") return null;\r\n\r\n // String vazia ou em branco é incomparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n if (valor.trim() === \"\") return null;\r\n\r\n const numero = Number(valor);\r\n if (Number.isFinite(numero)) return { tipo: \"number\", ordem: numero };\r\n\r\n const data = Date.parse(valor);\r\n if (!Number.isNaN(data)) return { tipo: \"date\", ordem: data };\r\n\r\n return { tipo: \"string\", ordem: valor };\r\n}\r\n\r\n/**\r\n * Classifica um valor segundo um tipo DECLARADO pelo chamador (forma de mapa).\r\n * Como o tipo veio declarado, não há adivinhação: o valor ou serve, ou a chave\r\n * é incomparável (e derruba a resposta nova). É também mais tolerante que a\r\n * detecção por valor — com\r\n * `\"number\"`, `100` e `\"101\"` comparam entre si sem problema, já que o\r\n * chamador afirmou que aquele campo é numérico.\r\n */\r\nfunction classificarComTipo(\r\n valor: unknown,\r\n tipo: TipoComparacao,\r\n): ValorComparavel | null {\r\n // String vazia ou em branco nunca é comparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n const texto = typeof valor === \"string\" ? valor.trim() : null;\r\n if (texto === \"\") return null;\r\n\r\n if (tipo === \"number\") {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const numero = Number(texto);\r\n return Number.isFinite(numero) ? { tipo, ordem: numero } : null;\r\n }\r\n\r\n if (tipo === \"date\") {\r\n // Um número aqui é lido como epoch em ms.\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const data = Date.parse(texto);\r\n return Number.isNaN(data) ? null : { tipo, ordem: data };\r\n }\r\n\r\n return texto === null ? null : { tipo, ordem: texto };\r\n}\r\n\r\n/**\r\n * Tipos conhecidos por NOME de chave. É o que permite ao front continuar\r\n * passando só os caminhos — `[\"idg\", \"summary.last_updated\"]` — e ainda assim\r\n * ter `idg` comparado como número e `last_updated` como data, sem depender de\r\n * adivinhação pelo valor.\r\n *\r\n * A busca é feita pelo caminho completo e, se não achar, pelo último trecho\r\n * dele: \"summary.last_updated\" cai em \"last_updated\". Nomes fora desta tabela\r\n * (e de `monotonicTypes`) continuam sendo detectados pelo valor.\r\n */\r\nconst TIPOS_POR_NOME: Record<string, MonotonicType> = {\r\n idg: \"number\",\r\n versao: \"number\",\r\n ballots_counted: \"number\",\r\n last_updated: \"date\",\r\n};\r\n\r\n/** De onde veio o tipo usado para comparar uma chave. */\r\ntype OrigemDoTipo = \"declarado\" | \"conhecido\" | \"detectado\";\r\n\r\n/** Uma chave já normalizada: o caminho, o tipo dela e de onde o tipo veio. */\r\ninterface ChaveMonotonica {\r\n chave: string;\r\n /** null -> detectar pelo valor. */\r\n tipo: TipoComparacao | null;\r\n origem: OrigemDoTipo;\r\n}\r\n\r\n/**\r\n * Procura o tipo de uma chave na tabela de nomes: primeiro pelo caminho\r\n * completo (\"summary.last_updated\"), depois só pelo último trecho\r\n * (\"last_updated\").\r\n */\r\nfunction tipoConhecido(\r\n chave: string,\r\n tabela: Record<string, MonotonicType>,\r\n): MonotonicType | null {\r\n if (chave in tabela) return tabela[chave];\r\n const ultimo = chave.slice(chave.lastIndexOf(\".\") + 1);\r\n return ultimo in tabela ? tabela[ultimo] : null;\r\n}\r\n\r\n/**\r\n * Reduz as três formas aceitas de `monotonicKey` a uma lista única, já com o\r\n * tipo de cada chave resolvido: o declarado no mapa vence; senão vale o que a\r\n * tabela de nomes souber; senão fica `null` e o tipo é detectado pelo valor.\r\n */\r\nfunction normalizarChaves(\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): ChaveMonotonica[] {\r\n const semTipo = (chave: string): ChaveMonotonica => {\r\n const conhecido = tipoConhecido(chave, tabela);\r\n return conhecido\r\n ? { chave, tipo: conhecido, origem: \"conhecido\" }\r\n : { chave, tipo: null, origem: \"detectado\" };\r\n };\r\n\r\n if (typeof monotonicKey === \"string\") return [semTipo(monotonicKey)];\r\n if (Array.isArray(monotonicKey)) return monotonicKey.map(semTipo);\r\n\r\n return Object.entries(monotonicKey).map(([chave, tipo]) => ({\r\n chave,\r\n tipo,\r\n origem: \"declarado\" as const,\r\n }));\r\n}\r\n\r\n/** `a > b`, já sabendo que os dois lados são do mesmo tipo. */\r\nfunction maior(a: number | string, b: number | string): boolean {\r\n return typeof a === \"string\" ? a > String(b) : a > Number(b);\r\n}\r\n\r\n/** O que uma chave decidiu ao comparar a versão antiga com a nova. */\r\ninterface VotoMonotonico {\r\n chave: string;\r\n /** Tipo usado na comparação; `null` quando a chave é incomparável. */\r\n tipo: TipoComparacao | null;\r\n /** Se o tipo veio do mapa, da tabela de nomes ou da detecção pelo valor. */\r\n origem: OrigemDoTipo;\r\n oldVal: unknown;\r\n newVal: unknown;\r\n /** Qualquer resultado diferente de \"avançou\" derruba a resposta nova. */\r\n voto: \"avançou\" | \"não avançou\" | \"incomparável\";\r\n}\r\n\r\ninterface VereditoMonotonico {\r\n /** true -> aceitar o dado novo; false -> manter o que está em cache. */\r\n avancou: boolean;\r\n detalhes: VotoMonotonico[];\r\n}\r\n\r\n/**\r\n * Compara todas as chaves monotônicas entre o dado em cache e o dado novo.\r\n *\r\n * Verificação estrita: o dado novo só entra quando TODAS as chaves cresceram.\r\n * Basta uma que não cresça para a resposta ser descartada — e uma chave\r\n * incomparável (ausente de um dos lados, ilegível, fora do tipo esperado ou\r\n * com tipos divergentes entre as versões) também não cresceu, então também\r\n * derruba a resposta. Só entra o que comprovadamente cresceu em todas.\r\n */\r\nfunction avaliarMonotonicidade(\r\n oldData: unknown,\r\n newData: unknown,\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): VereditoMonotonico {\r\n const detalhes: VotoMonotonico[] = [];\r\n // Sem nenhuma chave configurada não há regra a aplicar, e o dado novo passa.\r\n let todasCresceram = true;\r\n\r\n // Avalia TODAS as chaves: nenhuma decide sozinha e nenhuma interrompe o laço,\r\n // para que o log de debug mostre o estado de cada uma.\r\n for (const { chave, tipo, origem } of normalizarChaves(monotonicKey, tabela)) {\r\n const oldVal = getPath(oldData, chave);\r\n const newVal = getPath(newData, chave);\r\n\r\n const oldCmp = tipo ? classificarComTipo(oldVal, tipo) : classificar(oldVal);\r\n const newCmp = tipo ? classificarComTipo(newVal, tipo) : classificar(newVal);\r\n\r\n // Quando o tipo é conhecido, ele é quem manda — `100` e `\"101\"` sob\r\n // \"number\" são o mesmo campo. Quando foi detectado pelo valor, tipos\r\n // divergentes entre as versões (número de um lado, string do outro; string\r\n // numérica vs. string de data) são sinal de que o campo mudou de forma.\r\n const divergente =\r\n tipo === null &&\r\n (typeof oldVal !== typeof newVal || oldCmp?.tipo !== newCmp?.tipo);\r\n\r\n // Incomparável não é \"neutro\": se a lib não consegue afirmar que a chave\r\n // cresceu, ela não cresceu, e a resposta nova cai.\r\n if (oldCmp === null || newCmp === null || divergente) {\r\n todasCresceram = false;\r\n detalhes.push({ chave, tipo: null, origem, oldVal, newVal, voto: \"incomparável\" });\r\n continue;\r\n }\r\n\r\n const avancou = maior(newCmp.ordem, oldCmp.ordem);\r\n if (!avancou) todasCresceram = false;\r\n detalhes.push({\r\n chave,\r\n tipo: newCmp.tipo,\r\n origem,\r\n oldVal,\r\n newVal,\r\n voto: avancou ? \"avançou\" : \"não avançou\",\r\n });\r\n }\r\n\r\n return { avancou: todasCresceram, detalhes };\r\n}\r\n\r\n/** Status que, por padrão, NÃO acionam o fallback: o servidor já respondeu. */\r\nconst STATUS_SEM_FALLBACK_PADRAO = [404];\r\n\r\n/**\r\n * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.\r\n *\r\n * 4g / desconhecida 30 s — base\r\n * 3g 45 s — 1,5x\r\n * 2g 60 s — 2x\r\n * slow-2g 90 s — 3x; borda de cobertura, interior\r\n *\r\n * A folga cresce com a rede ruim para não matar uma resposta que ainda viria\r\n * num client lento. O custo aparece em `fallbackUrls`, porque a fila é\r\n * sequencial: o pior caso é N x o tier — veja `timeoutMs` em GetFetchOptions.\r\n */\r\nexport const TIMEOUTS_POR_REDE: Record<EffectiveConnectionType | \"desconhecida\", number> = {\r\n \"slow-2g\": 90_000,\r\n \"2g\": 60_000,\r\n \"3g\": 45_000,\r\n \"4g\": 30_000,\r\n desconhecida: 30_000,\r\n};\r\n\r\nconst REDES_CONHECIDAS: EffectiveConnectionType[] = [\"slow-2g\", \"2g\", \"3g\", \"4g\"];\r\n\r\n/**\r\n * Lê `navigator.connection.effectiveType` (Network Information API).\r\n * Só Chromium e Android implementam; Safari, Firefox e SSR devolvem `null` e\r\n * caem no tier `desconhecida`.\r\n */\r\nfunction detectarRede(): EffectiveConnectionType | null {\r\n try {\r\n if (typeof navigator === \"undefined\") return null;\r\n const nav = navigator as Navigator & {\r\n connection?: { effectiveType?: string };\r\n mozConnection?: { effectiveType?: string };\r\n webkitConnection?: { effectiveType?: string };\r\n };\r\n const conexao = nav.connection ?? nav.mozConnection ?? nav.webkitConnection;\r\n const tipo = conexao?.effectiveType;\r\n if (!tipo) return null;\r\n return REDES_CONHECIDAS.indexOf(tipo as EffectiveConnectionType) >= 0\r\n ? (tipo as EffectiveConnectionType)\r\n : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** Erro dedicado do timeout, para não subir como um `AbortError` sem contexto. */\r\nfunction erroDeTimeout(url: string, timeoutMs: number): Error {\r\n const err = new Error(`Timeout de ${timeoutMs}ms ao buscar ${url}`);\r\n err.name = \"TimeoutError\";\r\n return err;\r\n}\r\n\r\n/** O sinal de uma tentativa, junto com o que é preciso para limpá-lo depois. */\r\ninterface SinalDaTentativa {\r\n signal: AbortSignal | undefined;\r\n /** true quando quem abortou foi o timeout, e não o chamador. */\r\n estourou(): boolean;\r\n /** Limpa timer e listener. Sempre chamar no `finally`. */\r\n cancelar(): void;\r\n}\r\n\r\n/**\r\n * Monta o `signal` de UMA tentativa: o timeout da vez encadeado com o `signal`\r\n * que o chamador passou em `fetchOptions`, para que cancelar por fora continue\r\n * funcionando.\r\n *\r\n * Feito na mão com `AbortController` em vez de `AbortSignal.any` +\r\n * `AbortSignal.timeout`, que só existem em Safari 17.4+ e Node 20+. Também\r\n * evita o problema de um `AbortSignal.timeout` passado pelo chamador: o\r\n * relógio dele começa na criação, então viraria um orçamento da fila inteira,\r\n * deixando os fallbacks sem tempo nenhum.\r\n */\r\nfunction criarSinal(\r\n timeoutMs: number,\r\n externo: AbortSignal | null,\r\n): SinalDaTentativa {\r\n if (!(timeoutMs > 0) || typeof AbortController === \"undefined\") {\r\n return {\r\n signal: externo ?? undefined,\r\n estourou: () => false,\r\n cancelar: () => {},\r\n };\r\n }\r\n\r\n const ctrl = new AbortController();\r\n let porTimeout = false;\r\n\r\n const timer = setTimeout(() => {\r\n porTimeout = true;\r\n ctrl.abort();\r\n }, timeoutMs);\r\n\r\n const repassar = () => ctrl.abort();\r\n if (externo) {\r\n if (externo.aborted) ctrl.abort();\r\n else externo.addEventListener(\"abort\", repassar);\r\n }\r\n\r\n return {\r\n signal: ctrl.signal,\r\n estourou: () => porTimeout,\r\n cancelar: () => {\r\n clearTimeout(timer);\r\n externo?.removeEventListener(\"abort\", repassar);\r\n },\r\n };\r\n}\r\n\r\nfunction isQuotaError(err: unknown): boolean {\r\n return (\r\n err instanceof Error &&\r\n (err.name === \"QuotaExceededError\" ||\r\n err.name === \"NS_ERROR_DOM_QUOTA_REACHED\")\r\n );\r\n}\r\n\r\n/** Logger padrão do modo debug: imprime no console com um prefixo fixo. */\r\nfunction defaultLogger(message: string, details?: Record<string, unknown>): void {\r\n if (details) {\r\n console.debug(`[reqcache] ${message}`, details);\r\n } else {\r\n console.debug(`[reqcache] ${message}`);\r\n }\r\n}\r\n\r\n/** Retorna localStorage se existir e funcionar; senão null (SSR, modo privado...). */\r\nfunction getDefaultStorage(): StorageLike | null {\r\n try {\r\n if (typeof localStorage !== \"undefined\") {\r\n const probe = \"__reqcache_probe__\";\r\n localStorage.setItem(probe, \"1\");\r\n localStorage.removeItem(probe);\r\n return localStorage;\r\n }\r\n } catch {\r\n /* Safari em modo privado antigo lança aqui */\r\n }\r\n return null;\r\n}\r\n\r\n/** Instância pronta para uso, caso não queira configurar nada. */\r\nexport const requestCache = new RequestCache();\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/request-cache.ts"],"sourcesContent":["/**\r\n * request-cache\r\n * -------------\r\n * Cache de requisições HTTP em localStorage, pensado para cenários de\r\n * alto volume em curto período (ex.: apuração de eleições).\r\n *\r\n * Modelo de armazenamento:\r\n * Tudo fica sob UMA chave no storage, contendo um ARRAY de objetos.\r\n * Cada objeto é indexado pela `rota` (a URL requisitada):\r\n * [{ rota, data, createdAt, expiresAt, lastAccess }, ...]\r\n *\r\n * Comportamento:\r\n * 1. Primeira chamada -> faz o fetch e grava a entrada da rota no array.\r\n * 2. Durante o TTL -> devolve os dados do cache, sem tocar na rede.\r\n * 3. Após expirar -> SÓ revalida quando o client chamar de novo (lazy).\r\n * Se houver `monotonicKey`, só aceita o novo dado quando TODAS as chaves\r\n * tiverem AVANÇADO; senão mantém o antigo.\r\n *\r\n * Proteção de espaço (limite de ~5 MB do localStorage):\r\n * - `maxEntries`: ao gravar, se passar do limite, remove a rota menos\r\n * recentemente usada (LRU). Roda sozinho, sem timer.\r\n * - Em erro de quota, remove as entradas mais antigas e tenta de novo.\r\n * - `cleanup()`: remove rotas expiradas há mais de `graceSeconds`.\r\n *\r\n * Extras para o caso de eleição:\r\n * - Deduplicação: chamadas simultâneas à mesma rota disparam 1 só fetch.\r\n * - staleOnError: se a rede/API falhar, devolve o último dado válido.\r\n */\r\n\r\nexport interface StorageLike {\r\n getItem(key: string): string | null;\r\n setItem(key: string, value: string): void;\r\n removeItem(key: string): void;\r\n}\r\n\r\n/**\r\n * Como comparar o valor de uma chave monotônica:\r\n * - `\"number\"` — numérica. Aceita `number` e string numérica (\"2232575810\").\r\n * - `\"date\"` — por timestamp. Aceita o que o `Date.parse` resolver\r\n * (\"2026-10-04T18:23:00Z\") e também epoch em ms.\r\n * - `\"string\"` — lexicográfica.\r\n */\r\nexport type MonotonicType = \"number\" | \"date\" | \"string\";\r\n\r\n/**\r\n * Chave(s) monotônica(s). Três formas:\r\n * - `\"summary.ballots_counted\"` — uma chave, tipo detectado pelo valor.\r\n * - `[\"idg\", \"summary.last_updated\"]` — várias, tipo detectado pelo valor.\r\n * - `{ idg: \"number\", \"summary.last_updated\": \"date\" }` — várias, com o tipo\r\n * declarado por nome de chave (recomendado: não depende de adivinhação).\r\n */\r\nexport type MonotonicKey = string | string[] | Record<string, MonotonicType>;\r\n\r\n/**\r\n * Qualidade de rede estimada pelo browser (Network Information API).\r\n * É uma ESTIMATIVA baseada em latência e throughput recentes, não o rádio do\r\n * aparelho: um 4g congestionado é reportado como \"2g\" — que é exatamente o\r\n * caso que interessa aqui.\r\n */\r\nexport type EffectiveConnectionType = \"slow-2g\" | \"2g\" | \"3g\" | \"4g\";\r\n\r\n/**\r\n * Timeout de UMA tentativa de fetch, em ms, por qualidade de rede.\r\n * `desconhecida` é o que vale em SSR e nos browsers sem a Network Information\r\n * API (Safari e Firefox).\r\n */\r\nexport type TimeoutTiers = Partial<\r\n Record<EffectiveConnectionType | \"desconhecida\", number>\r\n>;\r\n\r\n/**\r\n * Falha de HTTP com o status preservado. É o que permite decidir se vale a\r\n * pena tentar o próximo domínio (503, sim) ou não (404).\r\n */\r\nexport class HttpError extends Error {\r\n constructor(\r\n readonly status: number,\r\n readonly url: string,\r\n ) {\r\n super(`HTTP ${status} ao buscar ${url}`);\r\n this.name = \"HttpError\";\r\n }\r\n}\r\n\r\nexport interface GetFetchOptions {\r\n /**\r\n * Caminho(s) da chave monotônica. Suporta aninhamento com ponto.\r\n *\r\n * monotonicKey: \"resultado.votosApurados\"\r\n * monotonicKey: [\"idg\", \"summary.last_updated\"]\r\n * monotonicKey: { idg: \"number\", \"summary.last_updated\": \"date\" }\r\n *\r\n * Com mais de uma chave, TODAS são comparadas e o dado novo só é aceito\r\n * quando todas concordam que ele é mais recente — se qualquer uma vier\r\n * igual ou menor, a resposta é descartada e o dado em cache é mantido.\r\n * A comparação é sempre estrita: o valor novo precisa ser MAIOR que o\r\n * guardado, nunca igual.\r\n *\r\n * O tipo de cada chave é resolvido nesta ordem:\r\n * 1. declarado na forma de mapa;\r\n * 2. conhecido pelo NOME da chave (`idg` é número, `last_updated` é data —\r\n * veja `TIPOS_POR_NOME`, extensível via `monotonicTypes` na config);\r\n * 3. detectado pelo valor: número, string que converte para número finito,\r\n * string que o `Date.parse` resolve e, por fim, lexicográfica.\r\n *\r\n * TODAS as chaves precisam ter crescido. Uma chave incomparável (ausente de\r\n * um dos lados, ilegível ou fora do tipo esperado) conta como \"não cresceu\"\r\n * e derruba a resposta nova junto com as demais.\r\n */\r\n monotonicKey?: MonotonicKey;\r\n\r\n /** Opções nativas repassadas ao fetch (headers, method, body, signal...). */\r\n fetchOptions?: RequestInit;\r\n\r\n /**\r\n * Se true (padrão), quando a nova requisição falhar e existir dado antigo,\r\n * devolve esse dado antigo em vez de lançar erro.\r\n */\r\n staleOnError?: boolean;\r\n\r\n /** fetch customizado — útil para testes ou ambientes sem fetch global. */\r\n fetcher?: typeof fetch;\r\n\r\n /**\r\n * URLs alternativas (outros domínios/APIs) tentadas em ordem, caso `url`\r\n * falhe. A rota do cache continua sendo `url` — as alternativas só entram\r\n * na requisição, não criam entradas novas no cache.\r\n */\r\n fallbackUrls?: string[];\r\n\r\n /**\r\n * Chamado quando uma URL falha e a lib vai tentar a próxima da lista\r\n * (`url` + `fallbackUrls`). Útil para observabilidade/log.\r\n */\r\n onFallback?: (failedUrl: string, error: unknown) => void;\r\n\r\n /**\r\n * Timeout de CADA tentativa, em ms. Quando omitido, sai da tabela por rede\r\n * (`timeoutTiers` na config). Vale por candidato, não pela fila toda: com\r\n * dois `fallbackUrls`, o pior caso é 3x esse valor.\r\n */\r\n timeoutMs?: number;\r\n\r\n /**\r\n * Status HTTP que NÃO devem acionar o fallback. Padrão: `[404]`.\r\n *\r\n * Um 404 é uma resposta correta do servidor — \"esse recurso não existe\" —,\r\n * não uma indisponibilidade. Repetir o mesmo caminho em outro domínio tende\r\n * a devolver o mesmo 404, só que N vezes mais devagar, então o erro sobe na\r\n * hora. Passe `[]` para voltar ao comportamento antigo (tentar todos).\r\n */\r\n statusSemFallback?: number[];\r\n}\r\n\r\n/** Uma entrada do cache. `rota` é a chave identificadora. */\r\nexport interface CacheEntry<T = unknown> {\r\n rota: string;\r\n data: T;\r\n createdAt: number;\r\n expiresAt: number;\r\n lastAccess: number;\r\n}\r\n\r\n/** Uma linha de log do modo debug. */\r\nexport interface DebugLogFn {\r\n (message: string, details?: Record<string, unknown>): void;\r\n}\r\n\r\nexport interface RequestCacheConfig {\r\n /** Storage a usar. Padrão: localStorage (se disponível). */\r\n storage?: StorageLike;\r\n /** Chave única onde o array é guardado. Padrão \"reqcache\". */\r\n storageKey?: string;\r\n /** Máximo de rotas em cache. Excedeu -> remove a menos usada (LRU). Padrão 100. */\r\n maxEntries?: number;\r\n\r\n /**\r\n * Se true, imprime logs detalhados de cada operação (fetch de rede,\r\n * cache hit, deduplicação, fallback, LRU, etc.). Padrão false.\r\n * Útil para identificar se estão sendo feitas mais chamadas de rede\r\n * do que o necessário. Pode ser ligado/desligado em runtime com\r\n * `setDebug()`.\r\n */\r\n debug?: boolean;\r\n\r\n /** Logger customizado usado quando `debug` está ligado. Padrão: `console.debug`. */\r\n logger?: DebugLogFn;\r\n\r\n /**\r\n * Tipos monotônicos adicionais, por nome de chave. Somado (e com prioridade\r\n * sobre) a tabela embutida `TIPOS_POR_NOME`, para que o chamador possa\r\n * continuar passando `monotonicKey` como lista de caminhos e ainda assim ter\r\n * a comparação certa:\r\n *\r\n * new RequestCache({ monotonicTypes: { data_apuracao: \"date\" } })\r\n * ...\r\n * getFetch(url, 10_000, { monotonicKey: [\"idg\", \"data_apuracao\"] })\r\n *\r\n * Aceita o caminho completo (\"summary.last_updated\") ou só o último trecho\r\n * (\"last_updated\").\r\n */\r\n monotonicTypes?: Record<string, MonotonicType>;\r\n\r\n /**\r\n * Timeout de cada tentativa de fetch, por qualidade de rede do client.\r\n * O que for passado aqui é mesclado sobre `TIMEOUTS_POR_REDE`:\r\n *\r\n * new RequestCache({ timeoutTiers: { \"slow-2g\": 120_000 } })\r\n *\r\n * Um `timeoutMs` na chamada tem prioridade sobre esta tabela.\r\n */\r\n timeoutTiers?: TimeoutTiers;\r\n}\r\n\r\nexport class RequestCache {\r\n private storage: StorageLike | null;\r\n private storageKey: string;\r\n private maxEntries: number;\r\n private inflight = new Map<string, Promise<unknown>>();\r\n /** Relógio de acesso monotônico: sempre cresce, mesmo com acessos no mesmo ms. */\r\n private tick = 0;\r\n private debug: boolean;\r\n private logger: DebugLogFn;\r\n private monotonicTypes: Record<string, MonotonicType>;\r\n private timeoutTiers: Record<EffectiveConnectionType | \"desconhecida\", number>;\r\n\r\n constructor(config: RequestCacheConfig = {}) {\r\n this.storageKey = config.storageKey ?? \"reqcache\";\r\n this.maxEntries = config.maxEntries ?? 100;\r\n this.storage = config.storage ?? getDefaultStorage();\r\n this.debug = config.debug ?? false;\r\n this.logger = config.logger ?? defaultLogger;\r\n this.monotonicTypes = { ...TIPOS_POR_NOME, ...(config.monotonicTypes ?? {}) };\r\n this.timeoutTiers = { ...TIMEOUTS_POR_REDE, ...(config.timeoutTiers ?? {}) };\r\n }\r\n\r\n /** Liga/desliga o modo debug em runtime, sem precisar recriar a instância. */\r\n setDebug(enabled: boolean): void {\r\n this.debug = enabled;\r\n }\r\n\r\n /**\r\n * Busca uma URL usando cache.\r\n * @param url Rota da requisição (também é a chave do cache).\r\n * @param ttlMs Tempo de cache em milissegundos (ex.: 10000).\r\n * @param options Regra monotônica, opções de fetch, etc.\r\n */\r\n async getFetch<T = unknown>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions = {},\r\n ): Promise<T> {\r\n const now = Date.now();\r\n const all = this.readAll();\r\n const cached = all.find((e) => e.rota === url) as CacheEntry<T> | undefined;\r\n\r\n // 1. Cache ainda válido -> devolve sem tocar na rede.\r\n if (cached && now < cached.expiresAt) {\r\n cached.lastAccess = this.nextAccess(); // marca uso p/ o LRU\r\n this.writeAll(all);\r\n this.log(\"cache hit\", { url, expiresAt: new Date(cached.expiresAt).toISOString() });\r\n return cached.data;\r\n }\r\n\r\n // 2. Deduplicação: se já há uma requisição em andamento p/ essa rota,\r\n // todas as chamadas concorrentes aguardam a mesma Promise.\r\n const pending = this.inflight.get(url) as Promise<T> | undefined;\r\n if (pending) {\r\n this.log(\"dedup: aguardando requisição em andamento\", { url });\r\n return pending;\r\n }\r\n\r\n this.log(cached ? \"cache expirado, revalidando\" : \"cache miss\", { url });\r\n\r\n const promise = this.revalidate<T>(url, ttlMs, options, cached ?? null)\r\n .finally(() => this.inflight.delete(url));\r\n\r\n this.inflight.set(url, promise);\r\n return promise;\r\n }\r\n\r\n /** Remove uma rota específica do cache. */\r\n invalidate(url: string): void {\r\n const all = this.readAll().filter((e) => e.rota !== url);\r\n this.writeAll(all);\r\n }\r\n\r\n /**\r\n * Remove rotas expiradas há mais de `graceSeconds` (padrão 3600 = 1h).\r\n * Chame na inicialização do app, ou de tempos em tempos.\r\n * @returns quantidade de rotas removidas.\r\n */\r\n cleanup(graceSeconds = 3600): number {\r\n const limite = Date.now() - graceSeconds * 1000;\r\n const all = this.readAll();\r\n const mantidas = all.filter((e) => e.expiresAt > limite);\r\n this.writeAll(mantidas);\r\n const removidas = all.length - mantidas.length;\r\n if (removidas > 0) this.log(\"cleanup: rotas expiradas removidas\", { removidas });\r\n return removidas;\r\n }\r\n\r\n /** Esvazia todo o cache. */\r\n clear(): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.removeItem(this.storageKey);\r\n } catch {\r\n /* storage indisponível */\r\n }\r\n }\r\n\r\n // --- interno ------------------------------------------------------------\r\n\r\n /** Retorna um número de acesso estritamente crescente (para o LRU). */\r\n private nextAccess(): number {\r\n this.tick = Math.max(Date.now(), this.tick + 1);\r\n return this.tick;\r\n }\r\n\r\n /** Emite uma linha de log, só quando o modo debug está ligado. */\r\n private log(message: string, details?: Record<string, unknown>): void {\r\n if (!this.debug) return;\r\n this.logger(message, details);\r\n }\r\n\r\n /**\r\n * Timeout de UMA tentativa, em ms. A ordem de prioridade é:\r\n * 1. `timeoutMs` passado na chamada;\r\n * 2. o tier da rede atual do client (`navigator.connection`);\r\n * 3. o tier `desconhecida`, usado em SSR, Safari e Firefox.\r\n *\r\n * A rede é relida a cada tentativa de propósito: numa apuração o client pode\r\n * sair do wi-fi e cair no 3g no meio da fila de fallback.\r\n */\r\n private resolverTimeout(options: GetFetchOptions): number {\r\n if (typeof options.timeoutMs === \"number\") return options.timeoutMs;\r\n return this.timeoutTiers[detectarRede() ?? \"desconhecida\"];\r\n }\r\n\r\n /**\r\n * Tenta cada URL da lista em ordem (primário, depois os `fallbackUrls`).\r\n * Devolve o JSON da primeira que responder OK; se todas falharem, lança o\r\n * último erro.\r\n *\r\n * Duas situações encerram a fila antes do fim, em vez de seguir para o\r\n * próximo candidato:\r\n * - um status listado em `statusSemFallback` (padrão: 404). É resposta do\r\n * servidor, não indisponibilidade — o outro domínio diria o mesmo.\r\n * - o `signal` do chamador abortado. Foi cancelamento de quem pediu;\r\n * insistir só trocaria o erro real pelo AbortError do último candidato.\r\n */\r\n private async fetchComRedundancia<T>(\r\n candidatos: string[],\r\n fetcher: typeof fetch,\r\n options: GetFetchOptions,\r\n ): Promise<T> {\r\n const semFallback = new Set(\r\n options.statusSemFallback ?? STATUS_SEM_FALLBACK_PADRAO,\r\n );\r\n const externo = options.fetchOptions?.signal ?? null;\r\n let ultimoErro: unknown;\r\n\r\n for (const candidato of candidatos) {\r\n if (externo?.aborted) {\r\n this.log(\"cancelado pelo chamador: fila interrompida\", { url: candidato });\r\n throw externo.reason ?? new Error(\"Requisição cancelada pelo chamador\");\r\n }\r\n\r\n const timeoutMs = this.resolverTimeout(options);\r\n const tentativa = criarSinal(timeoutMs, externo);\r\n this.log(\"fetch de rede\", { url: candidato, timeoutMs, rede: detectarRede() });\r\n\r\n try {\r\n const res = await fetcher(candidato, {\r\n ...options.fetchOptions,\r\n signal: tentativa.signal,\r\n });\r\n if (!res.ok) throw new HttpError(res.status, candidato);\r\n const json = (await res.json()) as T;\r\n this.log(\"fetch OK\", { url: candidato, status: res.status });\r\n return json;\r\n } catch (bruto) {\r\n // Um abort disparado pelo nosso timer chega aqui como AbortError\r\n // genérico; troca por um erro que diz o que de fato aconteceu.\r\n const err = tentativa.estourou()\r\n ? erroDeTimeout(candidato, timeoutMs)\r\n : bruto;\r\n\r\n this.log(\"fetch falhou\", { url: candidato, erro: String(err) });\r\n ultimoErro = err;\r\n options.onFallback?.(candidato, err);\r\n\r\n if (err instanceof HttpError && semFallback.has(err.status)) {\r\n this.log(\"status sem fallback: fila interrompida\", {\r\n url: candidato,\r\n status: err.status,\r\n });\r\n throw err;\r\n }\r\n } finally {\r\n // Obrigatório: sem isso sobra um timer de minutos pendurado por\r\n // tentativa, segurando o event loop no Node e vazando no browser.\r\n tentativa.cancelar();\r\n }\r\n }\r\n\r\n throw ultimoErro;\r\n }\r\n\r\n private async revalidate<T>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions,\r\n cached: CacheEntry<T> | null,\r\n ): Promise<T> {\r\n const fetcher = options.fetcher ?? fetch;\r\n const candidatos = [url, ...(options.fallbackUrls ?? [])];\r\n let fresh: T;\r\n\r\n try {\r\n fresh = await this.fetchComRedundancia<T>(candidatos, fetcher, options);\r\n } catch (err) {\r\n // Todos os domínios falharam. Se temos dado antigo e staleOnError, devolve o antigo.\r\n if (cached && (options.staleOnError ?? true)) {\r\n this.log(\"staleOnError: devolvendo dado antigo\", { url });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n throw err;\r\n }\r\n\r\n // 3. Regra monotônica: só aceita o novo dado se TODAS as chaves que\r\n // conseguiram votar tiverem avançado.\r\n if (cached && options.monotonicKey) {\r\n const veredito = avaliarMonotonicidade(\r\n cached.data,\r\n fresh,\r\n options.monotonicKey,\r\n this.monotonicTypes,\r\n );\r\n\r\n if (!veredito.avancou) {\r\n // Alguma chave não avançou -> mantém o dado antigo, só renova a expiração.\r\n this.log(\"regra monotônica: valor não cresceu, mantendo dado antigo\", {\r\n url,\r\n chaves: veredito.detalhes,\r\n });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n }\r\n\r\n // 4. Grava e devolve o dado novo.\r\n const now = Date.now();\r\n this.log(\"cache gravado\", { url, ttlMs });\r\n this.upsert<T>({\r\n rota: url,\r\n data: fresh,\r\n createdAt: now,\r\n expiresAt: now + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return fresh;\r\n }\r\n\r\n /** Insere ou atualiza uma rota, aplicando o limite LRU. */\r\n private upsert<T>(entry: CacheEntry<T>): void {\r\n const all = this.readAll().filter((e) => e.rota !== entry.rota);\r\n all.push(entry);\r\n this.evictIfNeeded(all);\r\n this.writeAll(all);\r\n }\r\n\r\n /** Enquanto passar de maxEntries, remove a rota menos recentemente usada. */\r\n private evictIfNeeded(all: CacheEntry[]): void {\r\n while (all.length > this.maxEntries) {\r\n let idxMaisAntigo = 0;\r\n for (let i = 1; i < all.length; i++) {\r\n if (all[i].lastAccess < all[idxMaisAntigo].lastAccess) idxMaisAntigo = i;\r\n }\r\n this.log(\"LRU: removendo rota menos usada\", { url: all[idxMaisAntigo].rota });\r\n all.splice(idxMaisAntigo, 1);\r\n }\r\n }\r\n\r\n private readAll(): CacheEntry[] {\r\n if (!this.storage) return [];\r\n try {\r\n const raw = this.storage.getItem(this.storageKey);\r\n if (!raw) return [];\r\n const parsed = JSON.parse(raw);\r\n return Array.isArray(parsed) ? (parsed as CacheEntry[]) : [];\r\n } catch {\r\n return []; // JSON corrompido -> trata como cache vazio\r\n }\r\n }\r\n\r\n private writeAll(all: CacheEntry[]): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(all));\r\n } catch (err) {\r\n // Quota estourada: remove os mais antigos e tenta de novo.\r\n if (isQuotaError(err) && all.length > 0) {\r\n const reduzido = [...all].sort((a, b) => a.lastAccess - b.lastAccess);\r\n reduzido.splice(0, Math.ceil(reduzido.length / 2)); // descarta metade\r\n this.log(\"quota excedida: descartando metade das entradas mais antigas\", {\r\n totalAntes: all.length,\r\n totalDepois: reduzido.length,\r\n });\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(reduzido));\r\n } catch {\r\n /* ainda assim falhou -> desiste de persistir; dados já retornam */\r\n }\r\n }\r\n // Falha silenciosa: os dados ainda são devolvidos ao chamador.\r\n }\r\n }\r\n}\r\n\r\n/** Lê um caminho aninhado (\"a.b.c\") de um objeto de forma segura. */\r\nfunction getPath(obj: unknown, path: string): unknown {\r\n return path.split(\".\").reduce<unknown>((acc, k) => {\r\n if (acc && typeof acc === \"object\" && k in (acc as object)) {\r\n return (acc as Record<string, unknown>)[k];\r\n }\r\n return undefined;\r\n }, obj);\r\n}\r\n\r\n/** Como um valor deve ser comparado, decidido pelo próprio valor. */\r\ntype TipoComparacao = \"number\" | \"date\" | \"string\";\r\n\r\n/** Um valor já classificado, pronto para ser comparado com outro do mesmo tipo. */\r\ninterface ValorComparavel {\r\n tipo: TipoComparacao;\r\n ordem: number | string;\r\n}\r\n\r\n/**\r\n * Descobre como comparar um valor. A ordem das checagens NÃO pode ser trocada:\r\n * `Date.parse` aceita strings numéricas curtas como data (`Date.parse(\"9\")` cai\r\n * em setembro, `Date.parse(\"10\")` em outubro), então um `idg` curto viraria\r\n * data se a checagem de data viesse antes da numérica. No sentido inverso não\r\n * há risco: `Number(\"2026-10-04T18:23:00Z\")` é NaN.\r\n *\r\n * Devolve `null` quando o valor é incomparável.\r\n */\r\nfunction classificar(valor: unknown): ValorComparavel | null {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo: \"number\", ordem: valor } : null;\r\n }\r\n if (typeof valor !== \"string\") return null;\r\n\r\n // String vazia ou em branco é incomparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n if (valor.trim() === \"\") return null;\r\n\r\n const numero = Number(valor);\r\n if (Number.isFinite(numero)) return { tipo: \"number\", ordem: numero };\r\n\r\n const data = Date.parse(valor);\r\n if (!Number.isNaN(data)) return { tipo: \"date\", ordem: data };\r\n\r\n return { tipo: \"string\", ordem: valor };\r\n}\r\n\r\n/**\r\n * Classifica um valor segundo um tipo DECLARADO pelo chamador (forma de mapa).\r\n * Como o tipo veio declarado, não há adivinhação: o valor ou serve, ou a chave\r\n * é incomparável (e derruba a resposta nova). É também mais tolerante que a\r\n * detecção por valor — com\r\n * `\"number\"`, `100` e `\"101\"` comparam entre si sem problema, já que o\r\n * chamador afirmou que aquele campo é numérico.\r\n */\r\nfunction classificarComTipo(\r\n valor: unknown,\r\n tipo: TipoComparacao,\r\n): ValorComparavel | null {\r\n // String vazia ou em branco nunca é comparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n const texto = typeof valor === \"string\" ? valor.trim() : null;\r\n if (texto === \"\") return null;\r\n\r\n if (tipo === \"number\") {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const numero = Number(texto);\r\n return Number.isFinite(numero) ? { tipo, ordem: numero } : null;\r\n }\r\n\r\n if (tipo === \"date\") {\r\n // Um número aqui é lido como epoch em ms.\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const data = Date.parse(texto);\r\n return Number.isNaN(data) ? null : { tipo, ordem: data };\r\n }\r\n\r\n return texto === null ? null : { tipo, ordem: texto };\r\n}\r\n\r\n/**\r\n * Tipos conhecidos por NOME de chave. É o que permite ao front continuar\r\n * passando só os caminhos — `[\"idg\", \"summary.last_updated\"]` — e ainda assim\r\n * ter `idg` comparado como número e `last_updated` como data, sem depender de\r\n * adivinhação pelo valor.\r\n *\r\n * A busca é feita pelo caminho completo e, se não achar, pelo último trecho\r\n * dele: \"summary.last_updated\" cai em \"last_updated\". Nomes fora desta tabela\r\n * (e de `monotonicTypes`) continuam sendo detectados pelo valor.\r\n */\r\nconst TIPOS_POR_NOME: Record<string, MonotonicType> = {\r\n idg: \"number\",\r\n versao: \"number\",\r\n ballots_counted: \"number\",\r\n last_updated: \"date\",\r\n};\r\n\r\n/** De onde veio o tipo usado para comparar uma chave. */\r\ntype OrigemDoTipo = \"declarado\" | \"conhecido\" | \"detectado\";\r\n\r\n/** Uma chave já normalizada: o caminho, o tipo dela e de onde o tipo veio. */\r\ninterface ChaveMonotonica {\r\n chave: string;\r\n /** null -> detectar pelo valor. */\r\n tipo: TipoComparacao | null;\r\n origem: OrigemDoTipo;\r\n}\r\n\r\n/**\r\n * Procura o tipo de uma chave na tabela de nomes: primeiro pelo caminho\r\n * completo (\"summary.last_updated\"), depois só pelo último trecho\r\n * (\"last_updated\").\r\n */\r\nfunction tipoConhecido(\r\n chave: string,\r\n tabela: Record<string, MonotonicType>,\r\n): MonotonicType | null {\r\n if (chave in tabela) return tabela[chave];\r\n const ultimo = chave.slice(chave.lastIndexOf(\".\") + 1);\r\n return ultimo in tabela ? tabela[ultimo] : null;\r\n}\r\n\r\n/**\r\n * Reduz as três formas aceitas de `monotonicKey` a uma lista única, já com o\r\n * tipo de cada chave resolvido: o declarado no mapa vence; senão vale o que a\r\n * tabela de nomes souber; senão fica `null` e o tipo é detectado pelo valor.\r\n */\r\nfunction normalizarChaves(\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): ChaveMonotonica[] {\r\n const semTipo = (chave: string): ChaveMonotonica => {\r\n const conhecido = tipoConhecido(chave, tabela);\r\n return conhecido\r\n ? { chave, tipo: conhecido, origem: \"conhecido\" }\r\n : { chave, tipo: null, origem: \"detectado\" };\r\n };\r\n\r\n if (typeof monotonicKey === \"string\") return [semTipo(monotonicKey)];\r\n if (Array.isArray(monotonicKey)) return monotonicKey.map(semTipo);\r\n\r\n return Object.entries(monotonicKey).map(([chave, tipo]) => ({\r\n chave,\r\n tipo,\r\n origem: \"declarado\" as const,\r\n }));\r\n}\r\n\r\n/** `a > b`, já sabendo que os dois lados são do mesmo tipo. */\r\nfunction maior(a: number | string, b: number | string): boolean {\r\n return typeof a === \"string\" ? a > String(b) : a > Number(b);\r\n}\r\n\r\n/** O que uma chave decidiu ao comparar a versão antiga com a nova. */\r\ninterface VotoMonotonico {\r\n chave: string;\r\n /** Tipo usado na comparação; `null` quando a chave é incomparável. */\r\n tipo: TipoComparacao | null;\r\n /** Se o tipo veio do mapa, da tabela de nomes ou da detecção pelo valor. */\r\n origem: OrigemDoTipo;\r\n oldVal: unknown;\r\n newVal: unknown;\r\n /** Qualquer resultado diferente de \"avançou\" derruba a resposta nova. */\r\n voto: \"avançou\" | \"não avançou\" | \"incomparável\";\r\n}\r\n\r\ninterface VereditoMonotonico {\r\n /** true -> aceitar o dado novo; false -> manter o que está em cache. */\r\n avancou: boolean;\r\n detalhes: VotoMonotonico[];\r\n}\r\n\r\n/**\r\n * Compara todas as chaves monotônicas entre o dado em cache e o dado novo.\r\n *\r\n * Verificação estrita: o dado novo só entra quando TODAS as chaves cresceram.\r\n * Basta uma que não cresça para a resposta ser descartada — e uma chave\r\n * incomparável (ausente de um dos lados, ilegível, fora do tipo esperado ou\r\n * com tipos divergentes entre as versões) também não cresceu, então também\r\n * derruba a resposta. Só entra o que comprovadamente cresceu em todas.\r\n */\r\nfunction avaliarMonotonicidade(\r\n oldData: unknown,\r\n newData: unknown,\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): VereditoMonotonico {\r\n const detalhes: VotoMonotonico[] = [];\r\n // Sem nenhuma chave configurada não há regra a aplicar, e o dado novo passa.\r\n let todasCresceram = true;\r\n\r\n // Avalia TODAS as chaves: nenhuma decide sozinha e nenhuma interrompe o laço,\r\n // para que o log de debug mostre o estado de cada uma.\r\n for (const { chave, tipo, origem } of normalizarChaves(monotonicKey, tabela)) {\r\n const oldVal = getPath(oldData, chave);\r\n const newVal = getPath(newData, chave);\r\n\r\n const oldCmp = tipo ? classificarComTipo(oldVal, tipo) : classificar(oldVal);\r\n const newCmp = tipo ? classificarComTipo(newVal, tipo) : classificar(newVal);\r\n\r\n // Quando o tipo é conhecido, ele é quem manda — `100` e `\"101\"` sob\r\n // \"number\" são o mesmo campo. Quando foi detectado pelo valor, tipos\r\n // divergentes entre as versões (número de um lado, string do outro; string\r\n // numérica vs. string de data) são sinal de que o campo mudou de forma.\r\n const divergente =\r\n tipo === null &&\r\n (typeof oldVal !== typeof newVal || oldCmp?.tipo !== newCmp?.tipo);\r\n\r\n // Incomparável não é \"neutro\": se a lib não consegue afirmar que a chave\r\n // cresceu, ela não cresceu, e a resposta nova cai.\r\n if (oldCmp === null || newCmp === null || divergente) {\r\n todasCresceram = false;\r\n detalhes.push({ chave, tipo: null, origem, oldVal, newVal, voto: \"incomparável\" });\r\n continue;\r\n }\r\n\r\n const avancou = maior(newCmp.ordem, oldCmp.ordem);\r\n if (!avancou) todasCresceram = false;\r\n detalhes.push({\r\n chave,\r\n tipo: newCmp.tipo,\r\n origem,\r\n oldVal,\r\n newVal,\r\n voto: avancou ? \"avançou\" : \"não avançou\",\r\n });\r\n }\r\n\r\n return { avancou: todasCresceram, detalhes };\r\n}\r\n\r\n/** Status que, por padrão, NÃO acionam o fallback: o servidor já respondeu. */\r\nconst STATUS_SEM_FALLBACK_PADRAO = [404];\r\n\r\n/**\r\n * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.\r\n *\r\n * 4g / desconhecida 8 s — base\r\n * 3g 12 s — 1,5x\r\n * 2g 16 s — 2x\r\n * slow-2g 24 s — 3x; borda de cobertura, interior\r\n *\r\n * A base fica logo abaixo de um ciclo de polling de 10 s: numa tela que\r\n * repete a chamada, a resposta que chega depois do próximo tick já nasce\r\n * velha — o tick novo traz dado mais fresco. Abortar a tempo deixa o ciclo\r\n * seguinte começar limpo, e o `staleOnError` cobre o buraco com o dado\r\n * anterior em vez de travar a UI.\r\n *\r\n * A folga ainda cresce com a rede ruim, e os tiers piores passam de um ciclo\r\n * de propósito: lá a deduplicação por rota faz o polling desacelerar sozinho,\r\n * o que é melhor do que martelar um client que não dá conta.\r\n *\r\n * O custo aparece em `fallbackUrls`, porque a fila é sequencial: o pior caso\r\n * é N x o tier — veja `timeoutMs` em GetFetchOptions.\r\n */\r\nexport const TIMEOUTS_POR_REDE: Record<EffectiveConnectionType | \"desconhecida\", number> = {\r\n \"slow-2g\": 24_000,\r\n \"2g\": 16_000,\r\n \"3g\": 12_000,\r\n \"4g\": 8_000,\r\n desconhecida: 8_000,\r\n};\r\n\r\nconst REDES_CONHECIDAS: EffectiveConnectionType[] = [\"slow-2g\", \"2g\", \"3g\", \"4g\"];\r\n\r\n/**\r\n * Lê `navigator.connection.effectiveType` (Network Information API).\r\n * Só Chromium e Android implementam; Safari, Firefox e SSR devolvem `null` e\r\n * caem no tier `desconhecida`.\r\n */\r\nfunction detectarRede(): EffectiveConnectionType | null {\r\n try {\r\n if (typeof navigator === \"undefined\") return null;\r\n const nav = navigator as Navigator & {\r\n connection?: { effectiveType?: string };\r\n mozConnection?: { effectiveType?: string };\r\n webkitConnection?: { effectiveType?: string };\r\n };\r\n const conexao = nav.connection ?? nav.mozConnection ?? nav.webkitConnection;\r\n const tipo = conexao?.effectiveType;\r\n if (!tipo) return null;\r\n return REDES_CONHECIDAS.indexOf(tipo as EffectiveConnectionType) >= 0\r\n ? (tipo as EffectiveConnectionType)\r\n : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** Erro dedicado do timeout, para não subir como um `AbortError` sem contexto. */\r\nfunction erroDeTimeout(url: string, timeoutMs: number): Error {\r\n const err = new Error(`Timeout de ${timeoutMs}ms ao buscar ${url}`);\r\n err.name = \"TimeoutError\";\r\n return err;\r\n}\r\n\r\n/** O sinal de uma tentativa, junto com o que é preciso para limpá-lo depois. */\r\ninterface SinalDaTentativa {\r\n signal: AbortSignal | undefined;\r\n /** true quando quem abortou foi o timeout, e não o chamador. */\r\n estourou(): boolean;\r\n /** Limpa timer e listener. Sempre chamar no `finally`. */\r\n cancelar(): void;\r\n}\r\n\r\n/**\r\n * Monta o `signal` de UMA tentativa: o timeout da vez encadeado com o `signal`\r\n * que o chamador passou em `fetchOptions`, para que cancelar por fora continue\r\n * funcionando.\r\n *\r\n * Feito na mão com `AbortController` em vez de `AbortSignal.any` +\r\n * `AbortSignal.timeout`, que só existem em Safari 17.4+ e Node 20+. Também\r\n * evita o problema de um `AbortSignal.timeout` passado pelo chamador: o\r\n * relógio dele começa na criação, então viraria um orçamento da fila inteira,\r\n * deixando os fallbacks sem tempo nenhum.\r\n */\r\nfunction criarSinal(\r\n timeoutMs: number,\r\n externo: AbortSignal | null,\r\n): SinalDaTentativa {\r\n if (!(timeoutMs > 0) || typeof AbortController === \"undefined\") {\r\n return {\r\n signal: externo ?? undefined,\r\n estourou: () => false,\r\n cancelar: () => {},\r\n };\r\n }\r\n\r\n const ctrl = new AbortController();\r\n let porTimeout = false;\r\n\r\n const timer = setTimeout(() => {\r\n porTimeout = true;\r\n ctrl.abort();\r\n }, timeoutMs);\r\n\r\n const repassar = () => ctrl.abort();\r\n if (externo) {\r\n if (externo.aborted) ctrl.abort();\r\n else externo.addEventListener(\"abort\", repassar);\r\n }\r\n\r\n return {\r\n signal: ctrl.signal,\r\n estourou: () => porTimeout,\r\n cancelar: () => {\r\n clearTimeout(timer);\r\n externo?.removeEventListener(\"abort\", repassar);\r\n },\r\n };\r\n}\r\n\r\nfunction isQuotaError(err: unknown): boolean {\r\n return (\r\n err instanceof Error &&\r\n (err.name === \"QuotaExceededError\" ||\r\n err.name === \"NS_ERROR_DOM_QUOTA_REACHED\")\r\n );\r\n}\r\n\r\n/** Logger padrão do modo debug: imprime no console com um prefixo fixo. */\r\nfunction defaultLogger(message: string, details?: Record<string, unknown>): void {\r\n if (details) {\r\n console.debug(`[reqcache] ${message}`, details);\r\n } else {\r\n console.debug(`[reqcache] ${message}`);\r\n }\r\n}\r\n\r\n/** Retorna localStorage se existir e funcionar; senão null (SSR, modo privado...). */\r\nfunction getDefaultStorage(): StorageLike | null {\r\n try {\r\n if (typeof localStorage !== \"undefined\") {\r\n const probe = \"__reqcache_probe__\";\r\n localStorage.setItem(probe, \"1\");\r\n localStorage.removeItem(probe);\r\n return localStorage;\r\n }\r\n } catch {\r\n /* Safari em modo privado antigo lança aqui */\r\n }\r\n return null;\r\n}\r\n\r\n/** Instância pronta para uso, caso não queira configurar nada. */\r\nexport const requestCache = new RequestCache();\r\n"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -497,21 +497,30 @@
497
497
  /**
498
498
  * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.
499
499
  *
500
- * 4g / desconhecida 30 s — base
501
- * 3g 45 s — 1,5x
502
- * 2g 60 s — 2x
503
- * slow-2g 90 s — 3x; borda de cobertura, interior
500
+ * 4g / desconhecida 8 s — base
501
+ * 3g 12 s — 1,5x
502
+ * 2g 16 s — 2x
503
+ * slow-2g 24 s — 3x; borda de cobertura, interior
504
504
  *
505
- * A folga cresce com a rede ruim para não matar uma resposta que ainda viria
506
- * num client lento. O custo aparece em `fallbackUrls`, porque a fila é
507
- * sequencial: o pior caso é N x o tier veja `timeoutMs` em GetFetchOptions.
505
+ * A base fica logo abaixo de um ciclo de polling de 10 s: numa tela que
506
+ * repete a chamada, a resposta que chega depois do próximo tick já nasce
507
+ * velha o tick novo traz dado mais fresco. Abortar a tempo deixa o ciclo
508
+ * seguinte começar limpo, e o `staleOnError` cobre o buraco com o dado
509
+ * anterior em vez de travar a UI.
510
+ *
511
+ * A folga ainda cresce com a rede ruim, e os tiers piores passam de um ciclo
512
+ * de propósito: lá a deduplicação por rota faz o polling desacelerar sozinho,
513
+ * o que é melhor do que martelar um client que não dá conta.
514
+ *
515
+ * O custo aparece em `fallbackUrls`, porque a fila é sequencial: o pior caso
516
+ * é N x o tier — veja `timeoutMs` em GetFetchOptions.
508
517
  */
509
518
  const TIMEOUTS_POR_REDE = {
510
- "slow-2g": 90000,
511
- "2g": 60000,
512
- "3g": 45000,
513
- "4g": 30000,
514
- desconhecida: 30000,
519
+ "slow-2g": 24000,
520
+ "2g": 16000,
521
+ "3g": 12000,
522
+ "4g": 8000,
523
+ desconhecida: 8000,
515
524
  };
516
525
  const REDES_CONHECIDAS = ["slow-2g", "2g", "3g", "4g"];
517
526
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/request-cache.ts"],"sourcesContent":["/**\r\n * request-cache\r\n * -------------\r\n * Cache de requisições HTTP em localStorage, pensado para cenários de\r\n * alto volume em curto período (ex.: apuração de eleições).\r\n *\r\n * Modelo de armazenamento:\r\n * Tudo fica sob UMA chave no storage, contendo um ARRAY de objetos.\r\n * Cada objeto é indexado pela `rota` (a URL requisitada):\r\n * [{ rota, data, createdAt, expiresAt, lastAccess }, ...]\r\n *\r\n * Comportamento:\r\n * 1. Primeira chamada -> faz o fetch e grava a entrada da rota no array.\r\n * 2. Durante o TTL -> devolve os dados do cache, sem tocar na rede.\r\n * 3. Após expirar -> SÓ revalida quando o client chamar de novo (lazy).\r\n * Se houver `monotonicKey`, só aceita o novo dado quando TODAS as chaves\r\n * tiverem AVANÇADO; senão mantém o antigo.\r\n *\r\n * Proteção de espaço (limite de ~5 MB do localStorage):\r\n * - `maxEntries`: ao gravar, se passar do limite, remove a rota menos\r\n * recentemente usada (LRU). Roda sozinho, sem timer.\r\n * - Em erro de quota, remove as entradas mais antigas e tenta de novo.\r\n * - `cleanup()`: remove rotas expiradas há mais de `graceSeconds`.\r\n *\r\n * Extras para o caso de eleição:\r\n * - Deduplicação: chamadas simultâneas à mesma rota disparam 1 só fetch.\r\n * - staleOnError: se a rede/API falhar, devolve o último dado válido.\r\n */\r\n\r\nexport interface StorageLike {\r\n getItem(key: string): string | null;\r\n setItem(key: string, value: string): void;\r\n removeItem(key: string): void;\r\n}\r\n\r\n/**\r\n * Como comparar o valor de uma chave monotônica:\r\n * - `\"number\"` — numérica. Aceita `number` e string numérica (\"2232575810\").\r\n * - `\"date\"` — por timestamp. Aceita o que o `Date.parse` resolver\r\n * (\"2026-10-04T18:23:00Z\") e também epoch em ms.\r\n * - `\"string\"` — lexicográfica.\r\n */\r\nexport type MonotonicType = \"number\" | \"date\" | \"string\";\r\n\r\n/**\r\n * Chave(s) monotônica(s). Três formas:\r\n * - `\"summary.ballots_counted\"` — uma chave, tipo detectado pelo valor.\r\n * - `[\"idg\", \"summary.last_updated\"]` — várias, tipo detectado pelo valor.\r\n * - `{ idg: \"number\", \"summary.last_updated\": \"date\" }` — várias, com o tipo\r\n * declarado por nome de chave (recomendado: não depende de adivinhação).\r\n */\r\nexport type MonotonicKey = string | string[] | Record<string, MonotonicType>;\r\n\r\n/**\r\n * Qualidade de rede estimada pelo browser (Network Information API).\r\n * É uma ESTIMATIVA baseada em latência e throughput recentes, não o rádio do\r\n * aparelho: um 4g congestionado é reportado como \"2g\" — que é exatamente o\r\n * caso que interessa aqui.\r\n */\r\nexport type EffectiveConnectionType = \"slow-2g\" | \"2g\" | \"3g\" | \"4g\";\r\n\r\n/**\r\n * Timeout de UMA tentativa de fetch, em ms, por qualidade de rede.\r\n * `desconhecida` é o que vale em SSR e nos browsers sem a Network Information\r\n * API (Safari e Firefox).\r\n */\r\nexport type TimeoutTiers = Partial<\r\n Record<EffectiveConnectionType | \"desconhecida\", number>\r\n>;\r\n\r\n/**\r\n * Falha de HTTP com o status preservado. É o que permite decidir se vale a\r\n * pena tentar o próximo domínio (503, sim) ou não (404).\r\n */\r\nexport class HttpError extends Error {\r\n constructor(\r\n readonly status: number,\r\n readonly url: string,\r\n ) {\r\n super(`HTTP ${status} ao buscar ${url}`);\r\n this.name = \"HttpError\";\r\n }\r\n}\r\n\r\nexport interface GetFetchOptions {\r\n /**\r\n * Caminho(s) da chave monotônica. Suporta aninhamento com ponto.\r\n *\r\n * monotonicKey: \"resultado.votosApurados\"\r\n * monotonicKey: [\"idg\", \"summary.last_updated\"]\r\n * monotonicKey: { idg: \"number\", \"summary.last_updated\": \"date\" }\r\n *\r\n * Com mais de uma chave, TODAS são comparadas e o dado novo só é aceito\r\n * quando todas concordam que ele é mais recente — se qualquer uma vier\r\n * igual ou menor, a resposta é descartada e o dado em cache é mantido.\r\n * A comparação é sempre estrita: o valor novo precisa ser MAIOR que o\r\n * guardado, nunca igual.\r\n *\r\n * O tipo de cada chave é resolvido nesta ordem:\r\n * 1. declarado na forma de mapa;\r\n * 2. conhecido pelo NOME da chave (`idg` é número, `last_updated` é data —\r\n * veja `TIPOS_POR_NOME`, extensível via `monotonicTypes` na config);\r\n * 3. detectado pelo valor: número, string que converte para número finito,\r\n * string que o `Date.parse` resolve e, por fim, lexicográfica.\r\n *\r\n * TODAS as chaves precisam ter crescido. Uma chave incomparável (ausente de\r\n * um dos lados, ilegível ou fora do tipo esperado) conta como \"não cresceu\"\r\n * e derruba a resposta nova junto com as demais.\r\n */\r\n monotonicKey?: MonotonicKey;\r\n\r\n /** Opções nativas repassadas ao fetch (headers, method, body, signal...). */\r\n fetchOptions?: RequestInit;\r\n\r\n /**\r\n * Se true (padrão), quando a nova requisição falhar e existir dado antigo,\r\n * devolve esse dado antigo em vez de lançar erro.\r\n */\r\n staleOnError?: boolean;\r\n\r\n /** fetch customizado — útil para testes ou ambientes sem fetch global. */\r\n fetcher?: typeof fetch;\r\n\r\n /**\r\n * URLs alternativas (outros domínios/APIs) tentadas em ordem, caso `url`\r\n * falhe. A rota do cache continua sendo `url` — as alternativas só entram\r\n * na requisição, não criam entradas novas no cache.\r\n */\r\n fallbackUrls?: string[];\r\n\r\n /**\r\n * Chamado quando uma URL falha e a lib vai tentar a próxima da lista\r\n * (`url` + `fallbackUrls`). Útil para observabilidade/log.\r\n */\r\n onFallback?: (failedUrl: string, error: unknown) => void;\r\n\r\n /**\r\n * Timeout de CADA tentativa, em ms. Quando omitido, sai da tabela por rede\r\n * (`timeoutTiers` na config). Vale por candidato, não pela fila toda: com\r\n * dois `fallbackUrls`, o pior caso é 3x esse valor.\r\n */\r\n timeoutMs?: number;\r\n\r\n /**\r\n * Status HTTP que NÃO devem acionar o fallback. Padrão: `[404]`.\r\n *\r\n * Um 404 é uma resposta correta do servidor — \"esse recurso não existe\" —,\r\n * não uma indisponibilidade. Repetir o mesmo caminho em outro domínio tende\r\n * a devolver o mesmo 404, só que N vezes mais devagar, então o erro sobe na\r\n * hora. Passe `[]` para voltar ao comportamento antigo (tentar todos).\r\n */\r\n statusSemFallback?: number[];\r\n}\r\n\r\n/** Uma entrada do cache. `rota` é a chave identificadora. */\r\nexport interface CacheEntry<T = unknown> {\r\n rota: string;\r\n data: T;\r\n createdAt: number;\r\n expiresAt: number;\r\n lastAccess: number;\r\n}\r\n\r\n/** Uma linha de log do modo debug. */\r\nexport interface DebugLogFn {\r\n (message: string, details?: Record<string, unknown>): void;\r\n}\r\n\r\nexport interface RequestCacheConfig {\r\n /** Storage a usar. Padrão: localStorage (se disponível). */\r\n storage?: StorageLike;\r\n /** Chave única onde o array é guardado. Padrão \"reqcache\". */\r\n storageKey?: string;\r\n /** Máximo de rotas em cache. Excedeu -> remove a menos usada (LRU). Padrão 100. */\r\n maxEntries?: number;\r\n\r\n /**\r\n * Se true, imprime logs detalhados de cada operação (fetch de rede,\r\n * cache hit, deduplicação, fallback, LRU, etc.). Padrão false.\r\n * Útil para identificar se estão sendo feitas mais chamadas de rede\r\n * do que o necessário. Pode ser ligado/desligado em runtime com\r\n * `setDebug()`.\r\n */\r\n debug?: boolean;\r\n\r\n /** Logger customizado usado quando `debug` está ligado. Padrão: `console.debug`. */\r\n logger?: DebugLogFn;\r\n\r\n /**\r\n * Tipos monotônicos adicionais, por nome de chave. Somado (e com prioridade\r\n * sobre) a tabela embutida `TIPOS_POR_NOME`, para que o chamador possa\r\n * continuar passando `monotonicKey` como lista de caminhos e ainda assim ter\r\n * a comparação certa:\r\n *\r\n * new RequestCache({ monotonicTypes: { data_apuracao: \"date\" } })\r\n * ...\r\n * getFetch(url, 10_000, { monotonicKey: [\"idg\", \"data_apuracao\"] })\r\n *\r\n * Aceita o caminho completo (\"summary.last_updated\") ou só o último trecho\r\n * (\"last_updated\").\r\n */\r\n monotonicTypes?: Record<string, MonotonicType>;\r\n\r\n /**\r\n * Timeout de cada tentativa de fetch, por qualidade de rede do client.\r\n * O que for passado aqui é mesclado sobre `TIMEOUTS_POR_REDE`:\r\n *\r\n * new RequestCache({ timeoutTiers: { \"slow-2g\": 120_000 } })\r\n *\r\n * Um `timeoutMs` na chamada tem prioridade sobre esta tabela.\r\n */\r\n timeoutTiers?: TimeoutTiers;\r\n}\r\n\r\nexport class RequestCache {\r\n private storage: StorageLike | null;\r\n private storageKey: string;\r\n private maxEntries: number;\r\n private inflight = new Map<string, Promise<unknown>>();\r\n /** Relógio de acesso monotônico: sempre cresce, mesmo com acessos no mesmo ms. */\r\n private tick = 0;\r\n private debug: boolean;\r\n private logger: DebugLogFn;\r\n private monotonicTypes: Record<string, MonotonicType>;\r\n private timeoutTiers: Record<EffectiveConnectionType | \"desconhecida\", number>;\r\n\r\n constructor(config: RequestCacheConfig = {}) {\r\n this.storageKey = config.storageKey ?? \"reqcache\";\r\n this.maxEntries = config.maxEntries ?? 100;\r\n this.storage = config.storage ?? getDefaultStorage();\r\n this.debug = config.debug ?? false;\r\n this.logger = config.logger ?? defaultLogger;\r\n this.monotonicTypes = { ...TIPOS_POR_NOME, ...(config.monotonicTypes ?? {}) };\r\n this.timeoutTiers = { ...TIMEOUTS_POR_REDE, ...(config.timeoutTiers ?? {}) };\r\n }\r\n\r\n /** Liga/desliga o modo debug em runtime, sem precisar recriar a instância. */\r\n setDebug(enabled: boolean): void {\r\n this.debug = enabled;\r\n }\r\n\r\n /**\r\n * Busca uma URL usando cache.\r\n * @param url Rota da requisição (também é a chave do cache).\r\n * @param ttlMs Tempo de cache em milissegundos (ex.: 10000).\r\n * @param options Regra monotônica, opções de fetch, etc.\r\n */\r\n async getFetch<T = unknown>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions = {},\r\n ): Promise<T> {\r\n const now = Date.now();\r\n const all = this.readAll();\r\n const cached = all.find((e) => e.rota === url) as CacheEntry<T> | undefined;\r\n\r\n // 1. Cache ainda válido -> devolve sem tocar na rede.\r\n if (cached && now < cached.expiresAt) {\r\n cached.lastAccess = this.nextAccess(); // marca uso p/ o LRU\r\n this.writeAll(all);\r\n this.log(\"cache hit\", { url, expiresAt: new Date(cached.expiresAt).toISOString() });\r\n return cached.data;\r\n }\r\n\r\n // 2. Deduplicação: se já há uma requisição em andamento p/ essa rota,\r\n // todas as chamadas concorrentes aguardam a mesma Promise.\r\n const pending = this.inflight.get(url) as Promise<T> | undefined;\r\n if (pending) {\r\n this.log(\"dedup: aguardando requisição em andamento\", { url });\r\n return pending;\r\n }\r\n\r\n this.log(cached ? \"cache expirado, revalidando\" : \"cache miss\", { url });\r\n\r\n const promise = this.revalidate<T>(url, ttlMs, options, cached ?? null)\r\n .finally(() => this.inflight.delete(url));\r\n\r\n this.inflight.set(url, promise);\r\n return promise;\r\n }\r\n\r\n /** Remove uma rota específica do cache. */\r\n invalidate(url: string): void {\r\n const all = this.readAll().filter((e) => e.rota !== url);\r\n this.writeAll(all);\r\n }\r\n\r\n /**\r\n * Remove rotas expiradas há mais de `graceSeconds` (padrão 3600 = 1h).\r\n * Chame na inicialização do app, ou de tempos em tempos.\r\n * @returns quantidade de rotas removidas.\r\n */\r\n cleanup(graceSeconds = 3600): number {\r\n const limite = Date.now() - graceSeconds * 1000;\r\n const all = this.readAll();\r\n const mantidas = all.filter((e) => e.expiresAt > limite);\r\n this.writeAll(mantidas);\r\n const removidas = all.length - mantidas.length;\r\n if (removidas > 0) this.log(\"cleanup: rotas expiradas removidas\", { removidas });\r\n return removidas;\r\n }\r\n\r\n /** Esvazia todo o cache. */\r\n clear(): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.removeItem(this.storageKey);\r\n } catch {\r\n /* storage indisponível */\r\n }\r\n }\r\n\r\n // --- interno ------------------------------------------------------------\r\n\r\n /** Retorna um número de acesso estritamente crescente (para o LRU). */\r\n private nextAccess(): number {\r\n this.tick = Math.max(Date.now(), this.tick + 1);\r\n return this.tick;\r\n }\r\n\r\n /** Emite uma linha de log, só quando o modo debug está ligado. */\r\n private log(message: string, details?: Record<string, unknown>): void {\r\n if (!this.debug) return;\r\n this.logger(message, details);\r\n }\r\n\r\n /**\r\n * Timeout de UMA tentativa, em ms. A ordem de prioridade é:\r\n * 1. `timeoutMs` passado na chamada;\r\n * 2. o tier da rede atual do client (`navigator.connection`);\r\n * 3. o tier `desconhecida`, usado em SSR, Safari e Firefox.\r\n *\r\n * A rede é relida a cada tentativa de propósito: numa apuração o client pode\r\n * sair do wi-fi e cair no 3g no meio da fila de fallback.\r\n */\r\n private resolverTimeout(options: GetFetchOptions): number {\r\n if (typeof options.timeoutMs === \"number\") return options.timeoutMs;\r\n return this.timeoutTiers[detectarRede() ?? \"desconhecida\"];\r\n }\r\n\r\n /**\r\n * Tenta cada URL da lista em ordem (primário, depois os `fallbackUrls`).\r\n * Devolve o JSON da primeira que responder OK; se todas falharem, lança o\r\n * último erro.\r\n *\r\n * Duas situações encerram a fila antes do fim, em vez de seguir para o\r\n * próximo candidato:\r\n * - um status listado em `statusSemFallback` (padrão: 404). É resposta do\r\n * servidor, não indisponibilidade — o outro domínio diria o mesmo.\r\n * - o `signal` do chamador abortado. Foi cancelamento de quem pediu;\r\n * insistir só trocaria o erro real pelo AbortError do último candidato.\r\n */\r\n private async fetchComRedundancia<T>(\r\n candidatos: string[],\r\n fetcher: typeof fetch,\r\n options: GetFetchOptions,\r\n ): Promise<T> {\r\n const semFallback = new Set(\r\n options.statusSemFallback ?? STATUS_SEM_FALLBACK_PADRAO,\r\n );\r\n const externo = options.fetchOptions?.signal ?? null;\r\n let ultimoErro: unknown;\r\n\r\n for (const candidato of candidatos) {\r\n if (externo?.aborted) {\r\n this.log(\"cancelado pelo chamador: fila interrompida\", { url: candidato });\r\n throw externo.reason ?? new Error(\"Requisição cancelada pelo chamador\");\r\n }\r\n\r\n const timeoutMs = this.resolverTimeout(options);\r\n const tentativa = criarSinal(timeoutMs, externo);\r\n this.log(\"fetch de rede\", { url: candidato, timeoutMs, rede: detectarRede() });\r\n\r\n try {\r\n const res = await fetcher(candidato, {\r\n ...options.fetchOptions,\r\n signal: tentativa.signal,\r\n });\r\n if (!res.ok) throw new HttpError(res.status, candidato);\r\n const json = (await res.json()) as T;\r\n this.log(\"fetch OK\", { url: candidato, status: res.status });\r\n return json;\r\n } catch (bruto) {\r\n // Um abort disparado pelo nosso timer chega aqui como AbortError\r\n // genérico; troca por um erro que diz o que de fato aconteceu.\r\n const err = tentativa.estourou()\r\n ? erroDeTimeout(candidato, timeoutMs)\r\n : bruto;\r\n\r\n this.log(\"fetch falhou\", { url: candidato, erro: String(err) });\r\n ultimoErro = err;\r\n options.onFallback?.(candidato, err);\r\n\r\n if (err instanceof HttpError && semFallback.has(err.status)) {\r\n this.log(\"status sem fallback: fila interrompida\", {\r\n url: candidato,\r\n status: err.status,\r\n });\r\n throw err;\r\n }\r\n } finally {\r\n // Obrigatório: sem isso sobra um timer de minutos pendurado por\r\n // tentativa, segurando o event loop no Node e vazando no browser.\r\n tentativa.cancelar();\r\n }\r\n }\r\n\r\n throw ultimoErro;\r\n }\r\n\r\n private async revalidate<T>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions,\r\n cached: CacheEntry<T> | null,\r\n ): Promise<T> {\r\n const fetcher = options.fetcher ?? fetch;\r\n const candidatos = [url, ...(options.fallbackUrls ?? [])];\r\n let fresh: T;\r\n\r\n try {\r\n fresh = await this.fetchComRedundancia<T>(candidatos, fetcher, options);\r\n } catch (err) {\r\n // Todos os domínios falharam. Se temos dado antigo e staleOnError, devolve o antigo.\r\n if (cached && (options.staleOnError ?? true)) {\r\n this.log(\"staleOnError: devolvendo dado antigo\", { url });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n throw err;\r\n }\r\n\r\n // 3. Regra monotônica: só aceita o novo dado se TODAS as chaves que\r\n // conseguiram votar tiverem avançado.\r\n if (cached && options.monotonicKey) {\r\n const veredito = avaliarMonotonicidade(\r\n cached.data,\r\n fresh,\r\n options.monotonicKey,\r\n this.monotonicTypes,\r\n );\r\n\r\n if (!veredito.avancou) {\r\n // Alguma chave não avançou -> mantém o dado antigo, só renova a expiração.\r\n this.log(\"regra monotônica: valor não cresceu, mantendo dado antigo\", {\r\n url,\r\n chaves: veredito.detalhes,\r\n });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n }\r\n\r\n // 4. Grava e devolve o dado novo.\r\n const now = Date.now();\r\n this.log(\"cache gravado\", { url, ttlMs });\r\n this.upsert<T>({\r\n rota: url,\r\n data: fresh,\r\n createdAt: now,\r\n expiresAt: now + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return fresh;\r\n }\r\n\r\n /** Insere ou atualiza uma rota, aplicando o limite LRU. */\r\n private upsert<T>(entry: CacheEntry<T>): void {\r\n const all = this.readAll().filter((e) => e.rota !== entry.rota);\r\n all.push(entry);\r\n this.evictIfNeeded(all);\r\n this.writeAll(all);\r\n }\r\n\r\n /** Enquanto passar de maxEntries, remove a rota menos recentemente usada. */\r\n private evictIfNeeded(all: CacheEntry[]): void {\r\n while (all.length > this.maxEntries) {\r\n let idxMaisAntigo = 0;\r\n for (let i = 1; i < all.length; i++) {\r\n if (all[i].lastAccess < all[idxMaisAntigo].lastAccess) idxMaisAntigo = i;\r\n }\r\n this.log(\"LRU: removendo rota menos usada\", { url: all[idxMaisAntigo].rota });\r\n all.splice(idxMaisAntigo, 1);\r\n }\r\n }\r\n\r\n private readAll(): CacheEntry[] {\r\n if (!this.storage) return [];\r\n try {\r\n const raw = this.storage.getItem(this.storageKey);\r\n if (!raw) return [];\r\n const parsed = JSON.parse(raw);\r\n return Array.isArray(parsed) ? (parsed as CacheEntry[]) : [];\r\n } catch {\r\n return []; // JSON corrompido -> trata como cache vazio\r\n }\r\n }\r\n\r\n private writeAll(all: CacheEntry[]): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(all));\r\n } catch (err) {\r\n // Quota estourada: remove os mais antigos e tenta de novo.\r\n if (isQuotaError(err) && all.length > 0) {\r\n const reduzido = [...all].sort((a, b) => a.lastAccess - b.lastAccess);\r\n reduzido.splice(0, Math.ceil(reduzido.length / 2)); // descarta metade\r\n this.log(\"quota excedida: descartando metade das entradas mais antigas\", {\r\n totalAntes: all.length,\r\n totalDepois: reduzido.length,\r\n });\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(reduzido));\r\n } catch {\r\n /* ainda assim falhou -> desiste de persistir; dados já retornam */\r\n }\r\n }\r\n // Falha silenciosa: os dados ainda são devolvidos ao chamador.\r\n }\r\n }\r\n}\r\n\r\n/** Lê um caminho aninhado (\"a.b.c\") de um objeto de forma segura. */\r\nfunction getPath(obj: unknown, path: string): unknown {\r\n return path.split(\".\").reduce<unknown>((acc, k) => {\r\n if (acc && typeof acc === \"object\" && k in (acc as object)) {\r\n return (acc as Record<string, unknown>)[k];\r\n }\r\n return undefined;\r\n }, obj);\r\n}\r\n\r\n/** Como um valor deve ser comparado, decidido pelo próprio valor. */\r\ntype TipoComparacao = \"number\" | \"date\" | \"string\";\r\n\r\n/** Um valor já classificado, pronto para ser comparado com outro do mesmo tipo. */\r\ninterface ValorComparavel {\r\n tipo: TipoComparacao;\r\n ordem: number | string;\r\n}\r\n\r\n/**\r\n * Descobre como comparar um valor. A ordem das checagens NÃO pode ser trocada:\r\n * `Date.parse` aceita strings numéricas curtas como data (`Date.parse(\"9\")` cai\r\n * em setembro, `Date.parse(\"10\")` em outubro), então um `idg` curto viraria\r\n * data se a checagem de data viesse antes da numérica. No sentido inverso não\r\n * há risco: `Number(\"2026-10-04T18:23:00Z\")` é NaN.\r\n *\r\n * Devolve `null` quando o valor é incomparável.\r\n */\r\nfunction classificar(valor: unknown): ValorComparavel | null {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo: \"number\", ordem: valor } : null;\r\n }\r\n if (typeof valor !== \"string\") return null;\r\n\r\n // String vazia ou em branco é incomparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n if (valor.trim() === \"\") return null;\r\n\r\n const numero = Number(valor);\r\n if (Number.isFinite(numero)) return { tipo: \"number\", ordem: numero };\r\n\r\n const data = Date.parse(valor);\r\n if (!Number.isNaN(data)) return { tipo: \"date\", ordem: data };\r\n\r\n return { tipo: \"string\", ordem: valor };\r\n}\r\n\r\n/**\r\n * Classifica um valor segundo um tipo DECLARADO pelo chamador (forma de mapa).\r\n * Como o tipo veio declarado, não há adivinhação: o valor ou serve, ou a chave\r\n * é incomparável (e derruba a resposta nova). É também mais tolerante que a\r\n * detecção por valor — com\r\n * `\"number\"`, `100` e `\"101\"` comparam entre si sem problema, já que o\r\n * chamador afirmou que aquele campo é numérico.\r\n */\r\nfunction classificarComTipo(\r\n valor: unknown,\r\n tipo: TipoComparacao,\r\n): ValorComparavel | null {\r\n // String vazia ou em branco nunca é comparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n const texto = typeof valor === \"string\" ? valor.trim() : null;\r\n if (texto === \"\") return null;\r\n\r\n if (tipo === \"number\") {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const numero = Number(texto);\r\n return Number.isFinite(numero) ? { tipo, ordem: numero } : null;\r\n }\r\n\r\n if (tipo === \"date\") {\r\n // Um número aqui é lido como epoch em ms.\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const data = Date.parse(texto);\r\n return Number.isNaN(data) ? null : { tipo, ordem: data };\r\n }\r\n\r\n return texto === null ? null : { tipo, ordem: texto };\r\n}\r\n\r\n/**\r\n * Tipos conhecidos por NOME de chave. É o que permite ao front continuar\r\n * passando só os caminhos — `[\"idg\", \"summary.last_updated\"]` — e ainda assim\r\n * ter `idg` comparado como número e `last_updated` como data, sem depender de\r\n * adivinhação pelo valor.\r\n *\r\n * A busca é feita pelo caminho completo e, se não achar, pelo último trecho\r\n * dele: \"summary.last_updated\" cai em \"last_updated\". Nomes fora desta tabela\r\n * (e de `monotonicTypes`) continuam sendo detectados pelo valor.\r\n */\r\nconst TIPOS_POR_NOME: Record<string, MonotonicType> = {\r\n idg: \"number\",\r\n versao: \"number\",\r\n ballots_counted: \"number\",\r\n last_updated: \"date\",\r\n};\r\n\r\n/** De onde veio o tipo usado para comparar uma chave. */\r\ntype OrigemDoTipo = \"declarado\" | \"conhecido\" | \"detectado\";\r\n\r\n/** Uma chave já normalizada: o caminho, o tipo dela e de onde o tipo veio. */\r\ninterface ChaveMonotonica {\r\n chave: string;\r\n /** null -> detectar pelo valor. */\r\n tipo: TipoComparacao | null;\r\n origem: OrigemDoTipo;\r\n}\r\n\r\n/**\r\n * Procura o tipo de uma chave na tabela de nomes: primeiro pelo caminho\r\n * completo (\"summary.last_updated\"), depois só pelo último trecho\r\n * (\"last_updated\").\r\n */\r\nfunction tipoConhecido(\r\n chave: string,\r\n tabela: Record<string, MonotonicType>,\r\n): MonotonicType | null {\r\n if (chave in tabela) return tabela[chave];\r\n const ultimo = chave.slice(chave.lastIndexOf(\".\") + 1);\r\n return ultimo in tabela ? tabela[ultimo] : null;\r\n}\r\n\r\n/**\r\n * Reduz as três formas aceitas de `monotonicKey` a uma lista única, já com o\r\n * tipo de cada chave resolvido: o declarado no mapa vence; senão vale o que a\r\n * tabela de nomes souber; senão fica `null` e o tipo é detectado pelo valor.\r\n */\r\nfunction normalizarChaves(\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): ChaveMonotonica[] {\r\n const semTipo = (chave: string): ChaveMonotonica => {\r\n const conhecido = tipoConhecido(chave, tabela);\r\n return conhecido\r\n ? { chave, tipo: conhecido, origem: \"conhecido\" }\r\n : { chave, tipo: null, origem: \"detectado\" };\r\n };\r\n\r\n if (typeof monotonicKey === \"string\") return [semTipo(monotonicKey)];\r\n if (Array.isArray(monotonicKey)) return monotonicKey.map(semTipo);\r\n\r\n return Object.entries(monotonicKey).map(([chave, tipo]) => ({\r\n chave,\r\n tipo,\r\n origem: \"declarado\" as const,\r\n }));\r\n}\r\n\r\n/** `a > b`, já sabendo que os dois lados são do mesmo tipo. */\r\nfunction maior(a: number | string, b: number | string): boolean {\r\n return typeof a === \"string\" ? a > String(b) : a > Number(b);\r\n}\r\n\r\n/** O que uma chave decidiu ao comparar a versão antiga com a nova. */\r\ninterface VotoMonotonico {\r\n chave: string;\r\n /** Tipo usado na comparação; `null` quando a chave é incomparável. */\r\n tipo: TipoComparacao | null;\r\n /** Se o tipo veio do mapa, da tabela de nomes ou da detecção pelo valor. */\r\n origem: OrigemDoTipo;\r\n oldVal: unknown;\r\n newVal: unknown;\r\n /** Qualquer resultado diferente de \"avançou\" derruba a resposta nova. */\r\n voto: \"avançou\" | \"não avançou\" | \"incomparável\";\r\n}\r\n\r\ninterface VereditoMonotonico {\r\n /** true -> aceitar o dado novo; false -> manter o que está em cache. */\r\n avancou: boolean;\r\n detalhes: VotoMonotonico[];\r\n}\r\n\r\n/**\r\n * Compara todas as chaves monotônicas entre o dado em cache e o dado novo.\r\n *\r\n * Verificação estrita: o dado novo só entra quando TODAS as chaves cresceram.\r\n * Basta uma que não cresça para a resposta ser descartada — e uma chave\r\n * incomparável (ausente de um dos lados, ilegível, fora do tipo esperado ou\r\n * com tipos divergentes entre as versões) também não cresceu, então também\r\n * derruba a resposta. Só entra o que comprovadamente cresceu em todas.\r\n */\r\nfunction avaliarMonotonicidade(\r\n oldData: unknown,\r\n newData: unknown,\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): VereditoMonotonico {\r\n const detalhes: VotoMonotonico[] = [];\r\n // Sem nenhuma chave configurada não há regra a aplicar, e o dado novo passa.\r\n let todasCresceram = true;\r\n\r\n // Avalia TODAS as chaves: nenhuma decide sozinha e nenhuma interrompe o laço,\r\n // para que o log de debug mostre o estado de cada uma.\r\n for (const { chave, tipo, origem } of normalizarChaves(monotonicKey, tabela)) {\r\n const oldVal = getPath(oldData, chave);\r\n const newVal = getPath(newData, chave);\r\n\r\n const oldCmp = tipo ? classificarComTipo(oldVal, tipo) : classificar(oldVal);\r\n const newCmp = tipo ? classificarComTipo(newVal, tipo) : classificar(newVal);\r\n\r\n // Quando o tipo é conhecido, ele é quem manda — `100` e `\"101\"` sob\r\n // \"number\" são o mesmo campo. Quando foi detectado pelo valor, tipos\r\n // divergentes entre as versões (número de um lado, string do outro; string\r\n // numérica vs. string de data) são sinal de que o campo mudou de forma.\r\n const divergente =\r\n tipo === null &&\r\n (typeof oldVal !== typeof newVal || oldCmp?.tipo !== newCmp?.tipo);\r\n\r\n // Incomparável não é \"neutro\": se a lib não consegue afirmar que a chave\r\n // cresceu, ela não cresceu, e a resposta nova cai.\r\n if (oldCmp === null || newCmp === null || divergente) {\r\n todasCresceram = false;\r\n detalhes.push({ chave, tipo: null, origem, oldVal, newVal, voto: \"incomparável\" });\r\n continue;\r\n }\r\n\r\n const avancou = maior(newCmp.ordem, oldCmp.ordem);\r\n if (!avancou) todasCresceram = false;\r\n detalhes.push({\r\n chave,\r\n tipo: newCmp.tipo,\r\n origem,\r\n oldVal,\r\n newVal,\r\n voto: avancou ? \"avançou\" : \"não avançou\",\r\n });\r\n }\r\n\r\n return { avancou: todasCresceram, detalhes };\r\n}\r\n\r\n/** Status que, por padrão, NÃO acionam o fallback: o servidor já respondeu. */\r\nconst STATUS_SEM_FALLBACK_PADRAO = [404];\r\n\r\n/**\r\n * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.\r\n *\r\n * 4g / desconhecida 30 s — base\r\n * 3g 45 s — 1,5x\r\n * 2g 60 s — 2x\r\n * slow-2g 90 s — 3x; borda de cobertura, interior\r\n *\r\n * A folga cresce com a rede ruim para não matar uma resposta que ainda viria\r\n * num client lento. O custo aparece em `fallbackUrls`, porque a fila é\r\n * sequencial: o pior caso é N x o tier — veja `timeoutMs` em GetFetchOptions.\r\n */\r\nexport const TIMEOUTS_POR_REDE: Record<EffectiveConnectionType | \"desconhecida\", number> = {\r\n \"slow-2g\": 90_000,\r\n \"2g\": 60_000,\r\n \"3g\": 45_000,\r\n \"4g\": 30_000,\r\n desconhecida: 30_000,\r\n};\r\n\r\nconst REDES_CONHECIDAS: EffectiveConnectionType[] = [\"slow-2g\", \"2g\", \"3g\", \"4g\"];\r\n\r\n/**\r\n * Lê `navigator.connection.effectiveType` (Network Information API).\r\n * Só Chromium e Android implementam; Safari, Firefox e SSR devolvem `null` e\r\n * caem no tier `desconhecida`.\r\n */\r\nfunction detectarRede(): EffectiveConnectionType | null {\r\n try {\r\n if (typeof navigator === \"undefined\") return null;\r\n const nav = navigator as Navigator & {\r\n connection?: { effectiveType?: string };\r\n mozConnection?: { effectiveType?: string };\r\n webkitConnection?: { effectiveType?: string };\r\n };\r\n const conexao = nav.connection ?? nav.mozConnection ?? nav.webkitConnection;\r\n const tipo = conexao?.effectiveType;\r\n if (!tipo) return null;\r\n return REDES_CONHECIDAS.indexOf(tipo as EffectiveConnectionType) >= 0\r\n ? (tipo as EffectiveConnectionType)\r\n : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** Erro dedicado do timeout, para não subir como um `AbortError` sem contexto. */\r\nfunction erroDeTimeout(url: string, timeoutMs: number): Error {\r\n const err = new Error(`Timeout de ${timeoutMs}ms ao buscar ${url}`);\r\n err.name = \"TimeoutError\";\r\n return err;\r\n}\r\n\r\n/** O sinal de uma tentativa, junto com o que é preciso para limpá-lo depois. */\r\ninterface SinalDaTentativa {\r\n signal: AbortSignal | undefined;\r\n /** true quando quem abortou foi o timeout, e não o chamador. */\r\n estourou(): boolean;\r\n /** Limpa timer e listener. Sempre chamar no `finally`. */\r\n cancelar(): void;\r\n}\r\n\r\n/**\r\n * Monta o `signal` de UMA tentativa: o timeout da vez encadeado com o `signal`\r\n * que o chamador passou em `fetchOptions`, para que cancelar por fora continue\r\n * funcionando.\r\n *\r\n * Feito na mão com `AbortController` em vez de `AbortSignal.any` +\r\n * `AbortSignal.timeout`, que só existem em Safari 17.4+ e Node 20+. Também\r\n * evita o problema de um `AbortSignal.timeout` passado pelo chamador: o\r\n * relógio dele começa na criação, então viraria um orçamento da fila inteira,\r\n * deixando os fallbacks sem tempo nenhum.\r\n */\r\nfunction criarSinal(\r\n timeoutMs: number,\r\n externo: AbortSignal | null,\r\n): SinalDaTentativa {\r\n if (!(timeoutMs > 0) || typeof AbortController === \"undefined\") {\r\n return {\r\n signal: externo ?? undefined,\r\n estourou: () => false,\r\n cancelar: () => {},\r\n };\r\n }\r\n\r\n const ctrl = new AbortController();\r\n let porTimeout = false;\r\n\r\n const timer = setTimeout(() => {\r\n porTimeout = true;\r\n ctrl.abort();\r\n }, timeoutMs);\r\n\r\n const repassar = () => ctrl.abort();\r\n if (externo) {\r\n if (externo.aborted) ctrl.abort();\r\n else externo.addEventListener(\"abort\", repassar);\r\n }\r\n\r\n return {\r\n signal: ctrl.signal,\r\n estourou: () => porTimeout,\r\n cancelar: () => {\r\n clearTimeout(timer);\r\n externo?.removeEventListener(\"abort\", repassar);\r\n },\r\n };\r\n}\r\n\r\nfunction isQuotaError(err: unknown): boolean {\r\n return (\r\n err instanceof Error &&\r\n (err.name === \"QuotaExceededError\" ||\r\n err.name === \"NS_ERROR_DOM_QUOTA_REACHED\")\r\n );\r\n}\r\n\r\n/** Logger padrão do modo debug: imprime no console com um prefixo fixo. */\r\nfunction defaultLogger(message: string, details?: Record<string, unknown>): void {\r\n if (details) {\r\n console.debug(`[reqcache] ${message}`, details);\r\n } else {\r\n console.debug(`[reqcache] ${message}`);\r\n }\r\n}\r\n\r\n/** Retorna localStorage se existir e funcionar; senão null (SSR, modo privado...). */\r\nfunction getDefaultStorage(): StorageLike | null {\r\n try {\r\n if (typeof localStorage !== \"undefined\") {\r\n const probe = \"__reqcache_probe__\";\r\n localStorage.setItem(probe, \"1\");\r\n localStorage.removeItem(probe);\r\n return localStorage;\r\n }\r\n } catch {\r\n /* Safari em modo privado antigo lança aqui */\r\n }\r\n return null;\r\n}\r\n\r\n/** Instância pronta para uso, caso não queira configurar nada. */\r\nexport const requestCache = new RequestCache();\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/request-cache.ts"],"sourcesContent":["/**\r\n * request-cache\r\n * -------------\r\n * Cache de requisições HTTP em localStorage, pensado para cenários de\r\n * alto volume em curto período (ex.: apuração de eleições).\r\n *\r\n * Modelo de armazenamento:\r\n * Tudo fica sob UMA chave no storage, contendo um ARRAY de objetos.\r\n * Cada objeto é indexado pela `rota` (a URL requisitada):\r\n * [{ rota, data, createdAt, expiresAt, lastAccess }, ...]\r\n *\r\n * Comportamento:\r\n * 1. Primeira chamada -> faz o fetch e grava a entrada da rota no array.\r\n * 2. Durante o TTL -> devolve os dados do cache, sem tocar na rede.\r\n * 3. Após expirar -> SÓ revalida quando o client chamar de novo (lazy).\r\n * Se houver `monotonicKey`, só aceita o novo dado quando TODAS as chaves\r\n * tiverem AVANÇADO; senão mantém o antigo.\r\n *\r\n * Proteção de espaço (limite de ~5 MB do localStorage):\r\n * - `maxEntries`: ao gravar, se passar do limite, remove a rota menos\r\n * recentemente usada (LRU). Roda sozinho, sem timer.\r\n * - Em erro de quota, remove as entradas mais antigas e tenta de novo.\r\n * - `cleanup()`: remove rotas expiradas há mais de `graceSeconds`.\r\n *\r\n * Extras para o caso de eleição:\r\n * - Deduplicação: chamadas simultâneas à mesma rota disparam 1 só fetch.\r\n * - staleOnError: se a rede/API falhar, devolve o último dado válido.\r\n */\r\n\r\nexport interface StorageLike {\r\n getItem(key: string): string | null;\r\n setItem(key: string, value: string): void;\r\n removeItem(key: string): void;\r\n}\r\n\r\n/**\r\n * Como comparar o valor de uma chave monotônica:\r\n * - `\"number\"` — numérica. Aceita `number` e string numérica (\"2232575810\").\r\n * - `\"date\"` — por timestamp. Aceita o que o `Date.parse` resolver\r\n * (\"2026-10-04T18:23:00Z\") e também epoch em ms.\r\n * - `\"string\"` — lexicográfica.\r\n */\r\nexport type MonotonicType = \"number\" | \"date\" | \"string\";\r\n\r\n/**\r\n * Chave(s) monotônica(s). Três formas:\r\n * - `\"summary.ballots_counted\"` — uma chave, tipo detectado pelo valor.\r\n * - `[\"idg\", \"summary.last_updated\"]` — várias, tipo detectado pelo valor.\r\n * - `{ idg: \"number\", \"summary.last_updated\": \"date\" }` — várias, com o tipo\r\n * declarado por nome de chave (recomendado: não depende de adivinhação).\r\n */\r\nexport type MonotonicKey = string | string[] | Record<string, MonotonicType>;\r\n\r\n/**\r\n * Qualidade de rede estimada pelo browser (Network Information API).\r\n * É uma ESTIMATIVA baseada em latência e throughput recentes, não o rádio do\r\n * aparelho: um 4g congestionado é reportado como \"2g\" — que é exatamente o\r\n * caso que interessa aqui.\r\n */\r\nexport type EffectiveConnectionType = \"slow-2g\" | \"2g\" | \"3g\" | \"4g\";\r\n\r\n/**\r\n * Timeout de UMA tentativa de fetch, em ms, por qualidade de rede.\r\n * `desconhecida` é o que vale em SSR e nos browsers sem a Network Information\r\n * API (Safari e Firefox).\r\n */\r\nexport type TimeoutTiers = Partial<\r\n Record<EffectiveConnectionType | \"desconhecida\", number>\r\n>;\r\n\r\n/**\r\n * Falha de HTTP com o status preservado. É o que permite decidir se vale a\r\n * pena tentar o próximo domínio (503, sim) ou não (404).\r\n */\r\nexport class HttpError extends Error {\r\n constructor(\r\n readonly status: number,\r\n readonly url: string,\r\n ) {\r\n super(`HTTP ${status} ao buscar ${url}`);\r\n this.name = \"HttpError\";\r\n }\r\n}\r\n\r\nexport interface GetFetchOptions {\r\n /**\r\n * Caminho(s) da chave monotônica. Suporta aninhamento com ponto.\r\n *\r\n * monotonicKey: \"resultado.votosApurados\"\r\n * monotonicKey: [\"idg\", \"summary.last_updated\"]\r\n * monotonicKey: { idg: \"number\", \"summary.last_updated\": \"date\" }\r\n *\r\n * Com mais de uma chave, TODAS são comparadas e o dado novo só é aceito\r\n * quando todas concordam que ele é mais recente — se qualquer uma vier\r\n * igual ou menor, a resposta é descartada e o dado em cache é mantido.\r\n * A comparação é sempre estrita: o valor novo precisa ser MAIOR que o\r\n * guardado, nunca igual.\r\n *\r\n * O tipo de cada chave é resolvido nesta ordem:\r\n * 1. declarado na forma de mapa;\r\n * 2. conhecido pelo NOME da chave (`idg` é número, `last_updated` é data —\r\n * veja `TIPOS_POR_NOME`, extensível via `monotonicTypes` na config);\r\n * 3. detectado pelo valor: número, string que converte para número finito,\r\n * string que o `Date.parse` resolve e, por fim, lexicográfica.\r\n *\r\n * TODAS as chaves precisam ter crescido. Uma chave incomparável (ausente de\r\n * um dos lados, ilegível ou fora do tipo esperado) conta como \"não cresceu\"\r\n * e derruba a resposta nova junto com as demais.\r\n */\r\n monotonicKey?: MonotonicKey;\r\n\r\n /** Opções nativas repassadas ao fetch (headers, method, body, signal...). */\r\n fetchOptions?: RequestInit;\r\n\r\n /**\r\n * Se true (padrão), quando a nova requisição falhar e existir dado antigo,\r\n * devolve esse dado antigo em vez de lançar erro.\r\n */\r\n staleOnError?: boolean;\r\n\r\n /** fetch customizado — útil para testes ou ambientes sem fetch global. */\r\n fetcher?: typeof fetch;\r\n\r\n /**\r\n * URLs alternativas (outros domínios/APIs) tentadas em ordem, caso `url`\r\n * falhe. A rota do cache continua sendo `url` — as alternativas só entram\r\n * na requisição, não criam entradas novas no cache.\r\n */\r\n fallbackUrls?: string[];\r\n\r\n /**\r\n * Chamado quando uma URL falha e a lib vai tentar a próxima da lista\r\n * (`url` + `fallbackUrls`). Útil para observabilidade/log.\r\n */\r\n onFallback?: (failedUrl: string, error: unknown) => void;\r\n\r\n /**\r\n * Timeout de CADA tentativa, em ms. Quando omitido, sai da tabela por rede\r\n * (`timeoutTiers` na config). Vale por candidato, não pela fila toda: com\r\n * dois `fallbackUrls`, o pior caso é 3x esse valor.\r\n */\r\n timeoutMs?: number;\r\n\r\n /**\r\n * Status HTTP que NÃO devem acionar o fallback. Padrão: `[404]`.\r\n *\r\n * Um 404 é uma resposta correta do servidor — \"esse recurso não existe\" —,\r\n * não uma indisponibilidade. Repetir o mesmo caminho em outro domínio tende\r\n * a devolver o mesmo 404, só que N vezes mais devagar, então o erro sobe na\r\n * hora. Passe `[]` para voltar ao comportamento antigo (tentar todos).\r\n */\r\n statusSemFallback?: number[];\r\n}\r\n\r\n/** Uma entrada do cache. `rota` é a chave identificadora. */\r\nexport interface CacheEntry<T = unknown> {\r\n rota: string;\r\n data: T;\r\n createdAt: number;\r\n expiresAt: number;\r\n lastAccess: number;\r\n}\r\n\r\n/** Uma linha de log do modo debug. */\r\nexport interface DebugLogFn {\r\n (message: string, details?: Record<string, unknown>): void;\r\n}\r\n\r\nexport interface RequestCacheConfig {\r\n /** Storage a usar. Padrão: localStorage (se disponível). */\r\n storage?: StorageLike;\r\n /** Chave única onde o array é guardado. Padrão \"reqcache\". */\r\n storageKey?: string;\r\n /** Máximo de rotas em cache. Excedeu -> remove a menos usada (LRU). Padrão 100. */\r\n maxEntries?: number;\r\n\r\n /**\r\n * Se true, imprime logs detalhados de cada operação (fetch de rede,\r\n * cache hit, deduplicação, fallback, LRU, etc.). Padrão false.\r\n * Útil para identificar se estão sendo feitas mais chamadas de rede\r\n * do que o necessário. Pode ser ligado/desligado em runtime com\r\n * `setDebug()`.\r\n */\r\n debug?: boolean;\r\n\r\n /** Logger customizado usado quando `debug` está ligado. Padrão: `console.debug`. */\r\n logger?: DebugLogFn;\r\n\r\n /**\r\n * Tipos monotônicos adicionais, por nome de chave. Somado (e com prioridade\r\n * sobre) a tabela embutida `TIPOS_POR_NOME`, para que o chamador possa\r\n * continuar passando `monotonicKey` como lista de caminhos e ainda assim ter\r\n * a comparação certa:\r\n *\r\n * new RequestCache({ monotonicTypes: { data_apuracao: \"date\" } })\r\n * ...\r\n * getFetch(url, 10_000, { monotonicKey: [\"idg\", \"data_apuracao\"] })\r\n *\r\n * Aceita o caminho completo (\"summary.last_updated\") ou só o último trecho\r\n * (\"last_updated\").\r\n */\r\n monotonicTypes?: Record<string, MonotonicType>;\r\n\r\n /**\r\n * Timeout de cada tentativa de fetch, por qualidade de rede do client.\r\n * O que for passado aqui é mesclado sobre `TIMEOUTS_POR_REDE`:\r\n *\r\n * new RequestCache({ timeoutTiers: { \"slow-2g\": 120_000 } })\r\n *\r\n * Um `timeoutMs` na chamada tem prioridade sobre esta tabela.\r\n */\r\n timeoutTiers?: TimeoutTiers;\r\n}\r\n\r\nexport class RequestCache {\r\n private storage: StorageLike | null;\r\n private storageKey: string;\r\n private maxEntries: number;\r\n private inflight = new Map<string, Promise<unknown>>();\r\n /** Relógio de acesso monotônico: sempre cresce, mesmo com acessos no mesmo ms. */\r\n private tick = 0;\r\n private debug: boolean;\r\n private logger: DebugLogFn;\r\n private monotonicTypes: Record<string, MonotonicType>;\r\n private timeoutTiers: Record<EffectiveConnectionType | \"desconhecida\", number>;\r\n\r\n constructor(config: RequestCacheConfig = {}) {\r\n this.storageKey = config.storageKey ?? \"reqcache\";\r\n this.maxEntries = config.maxEntries ?? 100;\r\n this.storage = config.storage ?? getDefaultStorage();\r\n this.debug = config.debug ?? false;\r\n this.logger = config.logger ?? defaultLogger;\r\n this.monotonicTypes = { ...TIPOS_POR_NOME, ...(config.monotonicTypes ?? {}) };\r\n this.timeoutTiers = { ...TIMEOUTS_POR_REDE, ...(config.timeoutTiers ?? {}) };\r\n }\r\n\r\n /** Liga/desliga o modo debug em runtime, sem precisar recriar a instância. */\r\n setDebug(enabled: boolean): void {\r\n this.debug = enabled;\r\n }\r\n\r\n /**\r\n * Busca uma URL usando cache.\r\n * @param url Rota da requisição (também é a chave do cache).\r\n * @param ttlMs Tempo de cache em milissegundos (ex.: 10000).\r\n * @param options Regra monotônica, opções de fetch, etc.\r\n */\r\n async getFetch<T = unknown>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions = {},\r\n ): Promise<T> {\r\n const now = Date.now();\r\n const all = this.readAll();\r\n const cached = all.find((e) => e.rota === url) as CacheEntry<T> | undefined;\r\n\r\n // 1. Cache ainda válido -> devolve sem tocar na rede.\r\n if (cached && now < cached.expiresAt) {\r\n cached.lastAccess = this.nextAccess(); // marca uso p/ o LRU\r\n this.writeAll(all);\r\n this.log(\"cache hit\", { url, expiresAt: new Date(cached.expiresAt).toISOString() });\r\n return cached.data;\r\n }\r\n\r\n // 2. Deduplicação: se já há uma requisição em andamento p/ essa rota,\r\n // todas as chamadas concorrentes aguardam a mesma Promise.\r\n const pending = this.inflight.get(url) as Promise<T> | undefined;\r\n if (pending) {\r\n this.log(\"dedup: aguardando requisição em andamento\", { url });\r\n return pending;\r\n }\r\n\r\n this.log(cached ? \"cache expirado, revalidando\" : \"cache miss\", { url });\r\n\r\n const promise = this.revalidate<T>(url, ttlMs, options, cached ?? null)\r\n .finally(() => this.inflight.delete(url));\r\n\r\n this.inflight.set(url, promise);\r\n return promise;\r\n }\r\n\r\n /** Remove uma rota específica do cache. */\r\n invalidate(url: string): void {\r\n const all = this.readAll().filter((e) => e.rota !== url);\r\n this.writeAll(all);\r\n }\r\n\r\n /**\r\n * Remove rotas expiradas há mais de `graceSeconds` (padrão 3600 = 1h).\r\n * Chame na inicialização do app, ou de tempos em tempos.\r\n * @returns quantidade de rotas removidas.\r\n */\r\n cleanup(graceSeconds = 3600): number {\r\n const limite = Date.now() - graceSeconds * 1000;\r\n const all = this.readAll();\r\n const mantidas = all.filter((e) => e.expiresAt > limite);\r\n this.writeAll(mantidas);\r\n const removidas = all.length - mantidas.length;\r\n if (removidas > 0) this.log(\"cleanup: rotas expiradas removidas\", { removidas });\r\n return removidas;\r\n }\r\n\r\n /** Esvazia todo o cache. */\r\n clear(): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.removeItem(this.storageKey);\r\n } catch {\r\n /* storage indisponível */\r\n }\r\n }\r\n\r\n // --- interno ------------------------------------------------------------\r\n\r\n /** Retorna um número de acesso estritamente crescente (para o LRU). */\r\n private nextAccess(): number {\r\n this.tick = Math.max(Date.now(), this.tick + 1);\r\n return this.tick;\r\n }\r\n\r\n /** Emite uma linha de log, só quando o modo debug está ligado. */\r\n private log(message: string, details?: Record<string, unknown>): void {\r\n if (!this.debug) return;\r\n this.logger(message, details);\r\n }\r\n\r\n /**\r\n * Timeout de UMA tentativa, em ms. A ordem de prioridade é:\r\n * 1. `timeoutMs` passado na chamada;\r\n * 2. o tier da rede atual do client (`navigator.connection`);\r\n * 3. o tier `desconhecida`, usado em SSR, Safari e Firefox.\r\n *\r\n * A rede é relida a cada tentativa de propósito: numa apuração o client pode\r\n * sair do wi-fi e cair no 3g no meio da fila de fallback.\r\n */\r\n private resolverTimeout(options: GetFetchOptions): number {\r\n if (typeof options.timeoutMs === \"number\") return options.timeoutMs;\r\n return this.timeoutTiers[detectarRede() ?? \"desconhecida\"];\r\n }\r\n\r\n /**\r\n * Tenta cada URL da lista em ordem (primário, depois os `fallbackUrls`).\r\n * Devolve o JSON da primeira que responder OK; se todas falharem, lança o\r\n * último erro.\r\n *\r\n * Duas situações encerram a fila antes do fim, em vez de seguir para o\r\n * próximo candidato:\r\n * - um status listado em `statusSemFallback` (padrão: 404). É resposta do\r\n * servidor, não indisponibilidade — o outro domínio diria o mesmo.\r\n * - o `signal` do chamador abortado. Foi cancelamento de quem pediu;\r\n * insistir só trocaria o erro real pelo AbortError do último candidato.\r\n */\r\n private async fetchComRedundancia<T>(\r\n candidatos: string[],\r\n fetcher: typeof fetch,\r\n options: GetFetchOptions,\r\n ): Promise<T> {\r\n const semFallback = new Set(\r\n options.statusSemFallback ?? STATUS_SEM_FALLBACK_PADRAO,\r\n );\r\n const externo = options.fetchOptions?.signal ?? null;\r\n let ultimoErro: unknown;\r\n\r\n for (const candidato of candidatos) {\r\n if (externo?.aborted) {\r\n this.log(\"cancelado pelo chamador: fila interrompida\", { url: candidato });\r\n throw externo.reason ?? new Error(\"Requisição cancelada pelo chamador\");\r\n }\r\n\r\n const timeoutMs = this.resolverTimeout(options);\r\n const tentativa = criarSinal(timeoutMs, externo);\r\n this.log(\"fetch de rede\", { url: candidato, timeoutMs, rede: detectarRede() });\r\n\r\n try {\r\n const res = await fetcher(candidato, {\r\n ...options.fetchOptions,\r\n signal: tentativa.signal,\r\n });\r\n if (!res.ok) throw new HttpError(res.status, candidato);\r\n const json = (await res.json()) as T;\r\n this.log(\"fetch OK\", { url: candidato, status: res.status });\r\n return json;\r\n } catch (bruto) {\r\n // Um abort disparado pelo nosso timer chega aqui como AbortError\r\n // genérico; troca por um erro que diz o que de fato aconteceu.\r\n const err = tentativa.estourou()\r\n ? erroDeTimeout(candidato, timeoutMs)\r\n : bruto;\r\n\r\n this.log(\"fetch falhou\", { url: candidato, erro: String(err) });\r\n ultimoErro = err;\r\n options.onFallback?.(candidato, err);\r\n\r\n if (err instanceof HttpError && semFallback.has(err.status)) {\r\n this.log(\"status sem fallback: fila interrompida\", {\r\n url: candidato,\r\n status: err.status,\r\n });\r\n throw err;\r\n }\r\n } finally {\r\n // Obrigatório: sem isso sobra um timer de minutos pendurado por\r\n // tentativa, segurando o event loop no Node e vazando no browser.\r\n tentativa.cancelar();\r\n }\r\n }\r\n\r\n throw ultimoErro;\r\n }\r\n\r\n private async revalidate<T>(\r\n url: string,\r\n ttlMs: number,\r\n options: GetFetchOptions,\r\n cached: CacheEntry<T> | null,\r\n ): Promise<T> {\r\n const fetcher = options.fetcher ?? fetch;\r\n const candidatos = [url, ...(options.fallbackUrls ?? [])];\r\n let fresh: T;\r\n\r\n try {\r\n fresh = await this.fetchComRedundancia<T>(candidatos, fetcher, options);\r\n } catch (err) {\r\n // Todos os domínios falharam. Se temos dado antigo e staleOnError, devolve o antigo.\r\n if (cached && (options.staleOnError ?? true)) {\r\n this.log(\"staleOnError: devolvendo dado antigo\", { url });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n throw err;\r\n }\r\n\r\n // 3. Regra monotônica: só aceita o novo dado se TODAS as chaves que\r\n // conseguiram votar tiverem avançado.\r\n if (cached && options.monotonicKey) {\r\n const veredito = avaliarMonotonicidade(\r\n cached.data,\r\n fresh,\r\n options.monotonicKey,\r\n this.monotonicTypes,\r\n );\r\n\r\n if (!veredito.avancou) {\r\n // Alguma chave não avançou -> mantém o dado antigo, só renova a expiração.\r\n this.log(\"regra monotônica: valor não cresceu, mantendo dado antigo\", {\r\n url,\r\n chaves: veredito.detalhes,\r\n });\r\n this.upsert({\r\n ...cached,\r\n expiresAt: Date.now() + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return cached.data;\r\n }\r\n }\r\n\r\n // 4. Grava e devolve o dado novo.\r\n const now = Date.now();\r\n this.log(\"cache gravado\", { url, ttlMs });\r\n this.upsert<T>({\r\n rota: url,\r\n data: fresh,\r\n createdAt: now,\r\n expiresAt: now + ttlMs,\r\n lastAccess: this.nextAccess(),\r\n });\r\n return fresh;\r\n }\r\n\r\n /** Insere ou atualiza uma rota, aplicando o limite LRU. */\r\n private upsert<T>(entry: CacheEntry<T>): void {\r\n const all = this.readAll().filter((e) => e.rota !== entry.rota);\r\n all.push(entry);\r\n this.evictIfNeeded(all);\r\n this.writeAll(all);\r\n }\r\n\r\n /** Enquanto passar de maxEntries, remove a rota menos recentemente usada. */\r\n private evictIfNeeded(all: CacheEntry[]): void {\r\n while (all.length > this.maxEntries) {\r\n let idxMaisAntigo = 0;\r\n for (let i = 1; i < all.length; i++) {\r\n if (all[i].lastAccess < all[idxMaisAntigo].lastAccess) idxMaisAntigo = i;\r\n }\r\n this.log(\"LRU: removendo rota menos usada\", { url: all[idxMaisAntigo].rota });\r\n all.splice(idxMaisAntigo, 1);\r\n }\r\n }\r\n\r\n private readAll(): CacheEntry[] {\r\n if (!this.storage) return [];\r\n try {\r\n const raw = this.storage.getItem(this.storageKey);\r\n if (!raw) return [];\r\n const parsed = JSON.parse(raw);\r\n return Array.isArray(parsed) ? (parsed as CacheEntry[]) : [];\r\n } catch {\r\n return []; // JSON corrompido -> trata como cache vazio\r\n }\r\n }\r\n\r\n private writeAll(all: CacheEntry[]): void {\r\n if (!this.storage) return;\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(all));\r\n } catch (err) {\r\n // Quota estourada: remove os mais antigos e tenta de novo.\r\n if (isQuotaError(err) && all.length > 0) {\r\n const reduzido = [...all].sort((a, b) => a.lastAccess - b.lastAccess);\r\n reduzido.splice(0, Math.ceil(reduzido.length / 2)); // descarta metade\r\n this.log(\"quota excedida: descartando metade das entradas mais antigas\", {\r\n totalAntes: all.length,\r\n totalDepois: reduzido.length,\r\n });\r\n try {\r\n this.storage.setItem(this.storageKey, JSON.stringify(reduzido));\r\n } catch {\r\n /* ainda assim falhou -> desiste de persistir; dados já retornam */\r\n }\r\n }\r\n // Falha silenciosa: os dados ainda são devolvidos ao chamador.\r\n }\r\n }\r\n}\r\n\r\n/** Lê um caminho aninhado (\"a.b.c\") de um objeto de forma segura. */\r\nfunction getPath(obj: unknown, path: string): unknown {\r\n return path.split(\".\").reduce<unknown>((acc, k) => {\r\n if (acc && typeof acc === \"object\" && k in (acc as object)) {\r\n return (acc as Record<string, unknown>)[k];\r\n }\r\n return undefined;\r\n }, obj);\r\n}\r\n\r\n/** Como um valor deve ser comparado, decidido pelo próprio valor. */\r\ntype TipoComparacao = \"number\" | \"date\" | \"string\";\r\n\r\n/** Um valor já classificado, pronto para ser comparado com outro do mesmo tipo. */\r\ninterface ValorComparavel {\r\n tipo: TipoComparacao;\r\n ordem: number | string;\r\n}\r\n\r\n/**\r\n * Descobre como comparar um valor. A ordem das checagens NÃO pode ser trocada:\r\n * `Date.parse` aceita strings numéricas curtas como data (`Date.parse(\"9\")` cai\r\n * em setembro, `Date.parse(\"10\")` em outubro), então um `idg` curto viraria\r\n * data se a checagem de data viesse antes da numérica. No sentido inverso não\r\n * há risco: `Number(\"2026-10-04T18:23:00Z\")` é NaN.\r\n *\r\n * Devolve `null` quando o valor é incomparável.\r\n */\r\nfunction classificar(valor: unknown): ValorComparavel | null {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo: \"number\", ordem: valor } : null;\r\n }\r\n if (typeof valor !== \"string\") return null;\r\n\r\n // String vazia ou em branco é incomparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n if (valor.trim() === \"\") return null;\r\n\r\n const numero = Number(valor);\r\n if (Number.isFinite(numero)) return { tipo: \"number\", ordem: numero };\r\n\r\n const data = Date.parse(valor);\r\n if (!Number.isNaN(data)) return { tipo: \"date\", ordem: data };\r\n\r\n return { tipo: \"string\", ordem: valor };\r\n}\r\n\r\n/**\r\n * Classifica um valor segundo um tipo DECLARADO pelo chamador (forma de mapa).\r\n * Como o tipo veio declarado, não há adivinhação: o valor ou serve, ou a chave\r\n * é incomparável (e derruba a resposta nova). É também mais tolerante que a\r\n * detecção por valor — com\r\n * `\"number\"`, `100` e `\"101\"` comparam entre si sem problema, já que o\r\n * chamador afirmou que aquele campo é numérico.\r\n */\r\nfunction classificarComTipo(\r\n valor: unknown,\r\n tipo: TipoComparacao,\r\n): ValorComparavel | null {\r\n // String vazia ou em branco nunca é comparável: `Number(\"\")` e `Number(\" \")`\r\n // devolvem 0, e um campo vazio viraria um \"zero\" comparável.\r\n const texto = typeof valor === \"string\" ? valor.trim() : null;\r\n if (texto === \"\") return null;\r\n\r\n if (tipo === \"number\") {\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const numero = Number(texto);\r\n return Number.isFinite(numero) ? { tipo, ordem: numero } : null;\r\n }\r\n\r\n if (tipo === \"date\") {\r\n // Um número aqui é lido como epoch em ms.\r\n if (typeof valor === \"number\") {\r\n return Number.isFinite(valor) ? { tipo, ordem: valor } : null;\r\n }\r\n if (texto === null) return null;\r\n const data = Date.parse(texto);\r\n return Number.isNaN(data) ? null : { tipo, ordem: data };\r\n }\r\n\r\n return texto === null ? null : { tipo, ordem: texto };\r\n}\r\n\r\n/**\r\n * Tipos conhecidos por NOME de chave. É o que permite ao front continuar\r\n * passando só os caminhos — `[\"idg\", \"summary.last_updated\"]` — e ainda assim\r\n * ter `idg` comparado como número e `last_updated` como data, sem depender de\r\n * adivinhação pelo valor.\r\n *\r\n * A busca é feita pelo caminho completo e, se não achar, pelo último trecho\r\n * dele: \"summary.last_updated\" cai em \"last_updated\". Nomes fora desta tabela\r\n * (e de `monotonicTypes`) continuam sendo detectados pelo valor.\r\n */\r\nconst TIPOS_POR_NOME: Record<string, MonotonicType> = {\r\n idg: \"number\",\r\n versao: \"number\",\r\n ballots_counted: \"number\",\r\n last_updated: \"date\",\r\n};\r\n\r\n/** De onde veio o tipo usado para comparar uma chave. */\r\ntype OrigemDoTipo = \"declarado\" | \"conhecido\" | \"detectado\";\r\n\r\n/** Uma chave já normalizada: o caminho, o tipo dela e de onde o tipo veio. */\r\ninterface ChaveMonotonica {\r\n chave: string;\r\n /** null -> detectar pelo valor. */\r\n tipo: TipoComparacao | null;\r\n origem: OrigemDoTipo;\r\n}\r\n\r\n/**\r\n * Procura o tipo de uma chave na tabela de nomes: primeiro pelo caminho\r\n * completo (\"summary.last_updated\"), depois só pelo último trecho\r\n * (\"last_updated\").\r\n */\r\nfunction tipoConhecido(\r\n chave: string,\r\n tabela: Record<string, MonotonicType>,\r\n): MonotonicType | null {\r\n if (chave in tabela) return tabela[chave];\r\n const ultimo = chave.slice(chave.lastIndexOf(\".\") + 1);\r\n return ultimo in tabela ? tabela[ultimo] : null;\r\n}\r\n\r\n/**\r\n * Reduz as três formas aceitas de `monotonicKey` a uma lista única, já com o\r\n * tipo de cada chave resolvido: o declarado no mapa vence; senão vale o que a\r\n * tabela de nomes souber; senão fica `null` e o tipo é detectado pelo valor.\r\n */\r\nfunction normalizarChaves(\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): ChaveMonotonica[] {\r\n const semTipo = (chave: string): ChaveMonotonica => {\r\n const conhecido = tipoConhecido(chave, tabela);\r\n return conhecido\r\n ? { chave, tipo: conhecido, origem: \"conhecido\" }\r\n : { chave, tipo: null, origem: \"detectado\" };\r\n };\r\n\r\n if (typeof monotonicKey === \"string\") return [semTipo(monotonicKey)];\r\n if (Array.isArray(monotonicKey)) return monotonicKey.map(semTipo);\r\n\r\n return Object.entries(monotonicKey).map(([chave, tipo]) => ({\r\n chave,\r\n tipo,\r\n origem: \"declarado\" as const,\r\n }));\r\n}\r\n\r\n/** `a > b`, já sabendo que os dois lados são do mesmo tipo. */\r\nfunction maior(a: number | string, b: number | string): boolean {\r\n return typeof a === \"string\" ? a > String(b) : a > Number(b);\r\n}\r\n\r\n/** O que uma chave decidiu ao comparar a versão antiga com a nova. */\r\ninterface VotoMonotonico {\r\n chave: string;\r\n /** Tipo usado na comparação; `null` quando a chave é incomparável. */\r\n tipo: TipoComparacao | null;\r\n /** Se o tipo veio do mapa, da tabela de nomes ou da detecção pelo valor. */\r\n origem: OrigemDoTipo;\r\n oldVal: unknown;\r\n newVal: unknown;\r\n /** Qualquer resultado diferente de \"avançou\" derruba a resposta nova. */\r\n voto: \"avançou\" | \"não avançou\" | \"incomparável\";\r\n}\r\n\r\ninterface VereditoMonotonico {\r\n /** true -> aceitar o dado novo; false -> manter o que está em cache. */\r\n avancou: boolean;\r\n detalhes: VotoMonotonico[];\r\n}\r\n\r\n/**\r\n * Compara todas as chaves monotônicas entre o dado em cache e o dado novo.\r\n *\r\n * Verificação estrita: o dado novo só entra quando TODAS as chaves cresceram.\r\n * Basta uma que não cresça para a resposta ser descartada — e uma chave\r\n * incomparável (ausente de um dos lados, ilegível, fora do tipo esperado ou\r\n * com tipos divergentes entre as versões) também não cresceu, então também\r\n * derruba a resposta. Só entra o que comprovadamente cresceu em todas.\r\n */\r\nfunction avaliarMonotonicidade(\r\n oldData: unknown,\r\n newData: unknown,\r\n monotonicKey: MonotonicKey,\r\n tabela: Record<string, MonotonicType>,\r\n): VereditoMonotonico {\r\n const detalhes: VotoMonotonico[] = [];\r\n // Sem nenhuma chave configurada não há regra a aplicar, e o dado novo passa.\r\n let todasCresceram = true;\r\n\r\n // Avalia TODAS as chaves: nenhuma decide sozinha e nenhuma interrompe o laço,\r\n // para que o log de debug mostre o estado de cada uma.\r\n for (const { chave, tipo, origem } of normalizarChaves(monotonicKey, tabela)) {\r\n const oldVal = getPath(oldData, chave);\r\n const newVal = getPath(newData, chave);\r\n\r\n const oldCmp = tipo ? classificarComTipo(oldVal, tipo) : classificar(oldVal);\r\n const newCmp = tipo ? classificarComTipo(newVal, tipo) : classificar(newVal);\r\n\r\n // Quando o tipo é conhecido, ele é quem manda — `100` e `\"101\"` sob\r\n // \"number\" são o mesmo campo. Quando foi detectado pelo valor, tipos\r\n // divergentes entre as versões (número de um lado, string do outro; string\r\n // numérica vs. string de data) são sinal de que o campo mudou de forma.\r\n const divergente =\r\n tipo === null &&\r\n (typeof oldVal !== typeof newVal || oldCmp?.tipo !== newCmp?.tipo);\r\n\r\n // Incomparável não é \"neutro\": se a lib não consegue afirmar que a chave\r\n // cresceu, ela não cresceu, e a resposta nova cai.\r\n if (oldCmp === null || newCmp === null || divergente) {\r\n todasCresceram = false;\r\n detalhes.push({ chave, tipo: null, origem, oldVal, newVal, voto: \"incomparável\" });\r\n continue;\r\n }\r\n\r\n const avancou = maior(newCmp.ordem, oldCmp.ordem);\r\n if (!avancou) todasCresceram = false;\r\n detalhes.push({\r\n chave,\r\n tipo: newCmp.tipo,\r\n origem,\r\n oldVal,\r\n newVal,\r\n voto: avancou ? \"avançou\" : \"não avançou\",\r\n });\r\n }\r\n\r\n return { avancou: todasCresceram, detalhes };\r\n}\r\n\r\n/** Status que, por padrão, NÃO acionam o fallback: o servidor já respondeu. */\r\nconst STATUS_SEM_FALLBACK_PADRAO = [404];\r\n\r\n/**\r\n * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.\r\n *\r\n * 4g / desconhecida 8 s — base\r\n * 3g 12 s — 1,5x\r\n * 2g 16 s — 2x\r\n * slow-2g 24 s — 3x; borda de cobertura, interior\r\n *\r\n * A base fica logo abaixo de um ciclo de polling de 10 s: numa tela que\r\n * repete a chamada, a resposta que chega depois do próximo tick já nasce\r\n * velha — o tick novo traz dado mais fresco. Abortar a tempo deixa o ciclo\r\n * seguinte começar limpo, e o `staleOnError` cobre o buraco com o dado\r\n * anterior em vez de travar a UI.\r\n *\r\n * A folga ainda cresce com a rede ruim, e os tiers piores passam de um ciclo\r\n * de propósito: lá a deduplicação por rota faz o polling desacelerar sozinho,\r\n * o que é melhor do que martelar um client que não dá conta.\r\n *\r\n * O custo aparece em `fallbackUrls`, porque a fila é sequencial: o pior caso\r\n * é N x o tier — veja `timeoutMs` em GetFetchOptions.\r\n */\r\nexport const TIMEOUTS_POR_REDE: Record<EffectiveConnectionType | \"desconhecida\", number> = {\r\n \"slow-2g\": 24_000,\r\n \"2g\": 16_000,\r\n \"3g\": 12_000,\r\n \"4g\": 8_000,\r\n desconhecida: 8_000,\r\n};\r\n\r\nconst REDES_CONHECIDAS: EffectiveConnectionType[] = [\"slow-2g\", \"2g\", \"3g\", \"4g\"];\r\n\r\n/**\r\n * Lê `navigator.connection.effectiveType` (Network Information API).\r\n * Só Chromium e Android implementam; Safari, Firefox e SSR devolvem `null` e\r\n * caem no tier `desconhecida`.\r\n */\r\nfunction detectarRede(): EffectiveConnectionType | null {\r\n try {\r\n if (typeof navigator === \"undefined\") return null;\r\n const nav = navigator as Navigator & {\r\n connection?: { effectiveType?: string };\r\n mozConnection?: { effectiveType?: string };\r\n webkitConnection?: { effectiveType?: string };\r\n };\r\n const conexao = nav.connection ?? nav.mozConnection ?? nav.webkitConnection;\r\n const tipo = conexao?.effectiveType;\r\n if (!tipo) return null;\r\n return REDES_CONHECIDAS.indexOf(tipo as EffectiveConnectionType) >= 0\r\n ? (tipo as EffectiveConnectionType)\r\n : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** Erro dedicado do timeout, para não subir como um `AbortError` sem contexto. */\r\nfunction erroDeTimeout(url: string, timeoutMs: number): Error {\r\n const err = new Error(`Timeout de ${timeoutMs}ms ao buscar ${url}`);\r\n err.name = \"TimeoutError\";\r\n return err;\r\n}\r\n\r\n/** O sinal de uma tentativa, junto com o que é preciso para limpá-lo depois. */\r\ninterface SinalDaTentativa {\r\n signal: AbortSignal | undefined;\r\n /** true quando quem abortou foi o timeout, e não o chamador. */\r\n estourou(): boolean;\r\n /** Limpa timer e listener. Sempre chamar no `finally`. */\r\n cancelar(): void;\r\n}\r\n\r\n/**\r\n * Monta o `signal` de UMA tentativa: o timeout da vez encadeado com o `signal`\r\n * que o chamador passou em `fetchOptions`, para que cancelar por fora continue\r\n * funcionando.\r\n *\r\n * Feito na mão com `AbortController` em vez de `AbortSignal.any` +\r\n * `AbortSignal.timeout`, que só existem em Safari 17.4+ e Node 20+. Também\r\n * evita o problema de um `AbortSignal.timeout` passado pelo chamador: o\r\n * relógio dele começa na criação, então viraria um orçamento da fila inteira,\r\n * deixando os fallbacks sem tempo nenhum.\r\n */\r\nfunction criarSinal(\r\n timeoutMs: number,\r\n externo: AbortSignal | null,\r\n): SinalDaTentativa {\r\n if (!(timeoutMs > 0) || typeof AbortController === \"undefined\") {\r\n return {\r\n signal: externo ?? undefined,\r\n estourou: () => false,\r\n cancelar: () => {},\r\n };\r\n }\r\n\r\n const ctrl = new AbortController();\r\n let porTimeout = false;\r\n\r\n const timer = setTimeout(() => {\r\n porTimeout = true;\r\n ctrl.abort();\r\n }, timeoutMs);\r\n\r\n const repassar = () => ctrl.abort();\r\n if (externo) {\r\n if (externo.aborted) ctrl.abort();\r\n else externo.addEventListener(\"abort\", repassar);\r\n }\r\n\r\n return {\r\n signal: ctrl.signal,\r\n estourou: () => porTimeout,\r\n cancelar: () => {\r\n clearTimeout(timer);\r\n externo?.removeEventListener(\"abort\", repassar);\r\n },\r\n };\r\n}\r\n\r\nfunction isQuotaError(err: unknown): boolean {\r\n return (\r\n err instanceof Error &&\r\n (err.name === \"QuotaExceededError\" ||\r\n err.name === \"NS_ERROR_DOM_QUOTA_REACHED\")\r\n );\r\n}\r\n\r\n/** Logger padrão do modo debug: imprime no console com um prefixo fixo. */\r\nfunction defaultLogger(message: string, details?: Record<string, unknown>): void {\r\n if (details) {\r\n console.debug(`[reqcache] ${message}`, details);\r\n } else {\r\n console.debug(`[reqcache] ${message}`);\r\n }\r\n}\r\n\r\n/** Retorna localStorage se existir e funcionar; senão null (SSR, modo privado...). */\r\nfunction getDefaultStorage(): StorageLike | null {\r\n try {\r\n if (typeof localStorage !== \"undefined\") {\r\n const probe = \"__reqcache_probe__\";\r\n localStorage.setItem(probe, \"1\");\r\n localStorage.removeItem(probe);\r\n return localStorage;\r\n }\r\n } catch {\r\n /* Safari em modo privado antigo lança aqui */\r\n }\r\n return null;\r\n}\r\n\r\n/** Instância pronta para uso, caso não queira configurar nada. */\r\nexport const requestCache = new RequestCache();\r\n"],"names":[],"mappings":"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"}
@@ -253,14 +253,23 @@ export declare class RequestCache {
253
253
  /**
254
254
  * Tier list de timeout por qualidade de rede, em ms, aplicado a CADA tentativa.
255
255
  *
256
- * 4g / desconhecida 30 s — base
257
- * 3g 45 s — 1,5x
258
- * 2g 60 s — 2x
259
- * slow-2g 90 s — 3x; borda de cobertura, interior
256
+ * 4g / desconhecida 8 s — base
257
+ * 3g 12 s — 1,5x
258
+ * 2g 16 s — 2x
259
+ * slow-2g 24 s — 3x; borda de cobertura, interior
260
260
  *
261
- * A folga cresce com a rede ruim para não matar uma resposta que ainda viria
262
- * num client lento. O custo aparece em `fallbackUrls`, porque a fila é
263
- * sequencial: o pior caso é N x o tier veja `timeoutMs` em GetFetchOptions.
261
+ * A base fica logo abaixo de um ciclo de polling de 10 s: numa tela que
262
+ * repete a chamada, a resposta que chega depois do próximo tick já nasce
263
+ * velha o tick novo traz dado mais fresco. Abortar a tempo deixa o ciclo
264
+ * seguinte começar limpo, e o `staleOnError` cobre o buraco com o dado
265
+ * anterior em vez de travar a UI.
266
+ *
267
+ * A folga ainda cresce com a rede ruim, e os tiers piores passam de um ciclo
268
+ * de propósito: lá a deduplicação por rota faz o polling desacelerar sozinho,
269
+ * o que é melhor do que martelar um client que não dá conta.
270
+ *
271
+ * O custo aparece em `fallbackUrls`, porque a fila é sequencial: o pior caso
272
+ * é N x o tier — veja `timeoutMs` em GetFetchOptions.
264
273
  */
265
274
  export declare const TIMEOUTS_POR_REDE: Record<EffectiveConnectionType | "desconhecida", number>;
266
275
  /** Instância pronta para uso, caso não queira configurar nada. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sevn/reqcache",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Cache de requisicoes HTTP em localStorage, com regra monotonica, deduplicacao e limite LRU. Pensado para cenarios de alto volume (ex.: apuracao de eleicoes).",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",