sih-br-mcp 0.12.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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/cache.d.ts +108 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +270 -0
- package/dist/cache.js.map +1 -0
- package/dist/data/brazil-regions.json +68 -0
- package/dist/data/cid-chapters.json +163 -0
- package/dist/data/csap-groups-cid9.json +4281 -0
- package/dist/data/csap-groups.json +262 -0
- package/dist/db/duckdb.d.ts +263 -0
- package/dist/db/duckdb.d.ts.map +1 -0
- package/dist/db/duckdb.js +837 -0
- package/dist/db/duckdb.js.map +1 -0
- package/dist/freshness.d.ts +101 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/freshness.js +303 -0
- package/dist/freshness.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1504 -0
- package/dist/index.js.map +1 -0
- package/dist/provenance.d.ts +217 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +534 -0
- package/dist/provenance.js.map +1 -0
- package/dist/utils/age-groups.d.ts +164 -0
- package/dist/utils/age-groups.d.ts.map +1 -0
- package/dist/utils/age-groups.js +112 -0
- package/dist/utils/age-groups.js.map +1 -0
- package/dist/utils/population.d.ts +39 -0
- package/dist/utils/population.d.ts.map +1 -0
- package/dist/utils/population.js +114 -0
- package/dist/utils/population.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sidney Bissoli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# sih-br-mcp
|
|
2
|
+
|
|
3
|
+
Servidor MCP (Model Context Protocol) para análise das internações hospitalares do
|
|
4
|
+
SUS (SIH/SUS, AIH reduzida) com foco em ICSAP — internações por condições sensíveis
|
|
5
|
+
à atenção primária. Doze ferramentas sobre cubos anuais de 1992 a 2025 (causas por
|
|
6
|
+
capítulo/grupo CID, séries mensais, ICSAP por município, taxas por 100 mil), com a
|
|
7
|
+
proveniência da safra em cada resposta.
|
|
8
|
+
|
|
9
|
+
## De onde vêm os dados
|
|
10
|
+
|
|
11
|
+
Este servidor é **consumidor** do canal público `sih/cubos/` do projeto
|
|
12
|
+
[healthbr-data](https://github.com/SidneyBissoli/healthbr-data):
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Ministério da Saúde / DATASUS (RD<UF><AAMM>.dbc, FTP)
|
|
16
|
+
→ healthbr-data sih/rd/ (Parquet 1:1, manifesto com MD5 e data de download)
|
|
17
|
+
→ healthbr-data pipeline sih-cubos (scripts/pipeline/sih-cubos/build-aggregations.R)
|
|
18
|
+
→ https://data.sidneybissoli.com/sih/cubos/ (cubos + sidecar por ano + manifest.json + tables/)
|
|
19
|
+
→ este servidor (cache local sob demanda, SHA-256 conferido contra o manifesto)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Até 08/09/2026 o builder dos cubos vivia aqui (`scripts/build-aggregations.R`,
|
|
23
|
+
`rebuild-cubes.yml`); desde então o produtor é o healthbr-data e este repositório
|
|
24
|
+
não gera nem publica cubo nenhum (CONTEXT.md, decisão 27). A receita completa está
|
|
25
|
+
em `healthbr-data/scripts/pipeline/sih-cubos/README.md` e no card
|
|
26
|
+
[`sih-cubos`](https://github.com/SidneyBissoli/healthbr-data/blob/master/guides/dataset-cards/sih-cubos-README.md).
|
|
27
|
+
|
|
28
|
+
- **Cubos:** baixados por ano, só os que a chamada pede, para
|
|
29
|
+
`~/.cache/sih-br-mcp/cubos/` (`SIH_CACHE_DIR`), com o sidecar
|
|
30
|
+
`sih_provenance_<ano>.json` ao lado. `SIH_CUBES_BASE_URL` aponta outro canal;
|
|
31
|
+
`SIH_CUBES_CACHE=off` desliga (smoke e golden usam).
|
|
32
|
+
- **Frescor:** `src/freshness.ts` compara o sidecar com `sih/rd/manifest-summary.json`
|
|
33
|
+
e avisa quando um cubo está atrás do espelho; quem reconstrói é o produtor
|
|
34
|
+
(`rebuild-sih-cubes.yml`, toda terça e após cada manutenção do espelho).
|
|
35
|
+
- **Tabelas de classificação** (`src/data/`): cópias do contrato publicado em
|
|
36
|
+
`sih/cubos/tables/`; `npm run tables:check` confere o SHA-256 contra o manifesto
|
|
37
|
+
(roda no CI). Nunca edite aqui — a fonte é o produtor.
|
|
38
|
+
- **População** (`pop_uf.parquet`, `pop_uf_agregado.parquet`, `pop_municipios.parquet`):
|
|
39
|
+
desde a 0.12.0 vem do mesmo canal, assinada no bloco `population` do `manifest.json`
|
|
40
|
+
(produtor: `build-population.R` + `build-sih-population.yml` do healthbr-data — IBGE,
|
|
41
|
+
Projeção 2024 por UF; DATASUS POPBR/POPSVS por município). As ferramentas de taxa
|
|
42
|
+
(`get_hospitalization_rates`, `compare_icsap_trends` com `rate_per_10k`) e
|
|
43
|
+
`get_available_years` baixam os três arquivos para o cache na primeira chamada,
|
|
44
|
+
com SHA-256 conferido; uma pasta de dados que já tenha `pop_uf.parquet` tem
|
|
45
|
+
precedência (fixture, build local). A proveniência da população responde com o
|
|
46
|
+
`built_at` do manifesto.
|
|
47
|
+
|
|
48
|
+
## Uso
|
|
49
|
+
|
|
50
|
+
Pacote no npm: [`sih-br-mcp`](https://www.npmjs.com/package/sih-br-mcp) (Node 22+).
|
|
51
|
+
Ele não embarca dado nenhum — cubos, tabelas e população vêm do canal na primeira
|
|
52
|
+
chamada e ficam no cache local.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx -y sih-br-mcp # stdio
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Configuração num cliente MCP (Claude Desktop, Claude Code):
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{ "mcpServers": { "sih": { "command": "npx", "args": ["-y", "sih-br-mcp"] } } }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A partir do código-fonte:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install
|
|
68
|
+
npm run build
|
|
69
|
+
node dist/index.js # stdio
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Variáveis: `SIH_DATA_DIR` (pasta com cubos já prontos, em vez do cache),
|
|
73
|
+
`SIH_CACHE_DIR`, `SIH_CUBES_BASE_URL`, `SIH_CUBES_CACHE=off`,
|
|
74
|
+
`SIH_FRESHNESS_CHECK=off`.
|
|
75
|
+
|
|
76
|
+
## Verificação
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm ci && npm run build
|
|
80
|
+
npm run smoke:stdio # superfície das ferramentas × baselines/surface-stdio.json
|
|
81
|
+
npm run golden:tools # 12 ferramentas byte a byte × baselines/golden-tools.json (fixture 2023/RR)
|
|
82
|
+
npm run freshness:selftest # frescor offline sobre um trecho versionado do manifesto
|
|
83
|
+
npm run cache:selftest # cache local (download + SHA-256) contra um canal falso
|
|
84
|
+
npm run tables:check # tabelas de src/data × manifesto do canal
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
O CI (`.github/workflows/ci.yml`) roda tudo isso em Node 22 e 24. A fixture
|
|
88
|
+
`tests/fixtures/sih/` é uma cópia real dos cubos de 2023/RR gerados pelo builder
|
|
89
|
+
(hoje no healthbr-data) — é o que torna medível qualquer bump.
|
|
90
|
+
|
|
91
|
+
## Documentação
|
|
92
|
+
|
|
93
|
+
- `CONTEXT.md` — decisões arquiteturais numeradas (a 27 é a migração do produtor).
|
|
94
|
+
- `docs/analise-001` (janela de competências), `analise-002` (era CID-9, 1992–1997),
|
|
95
|
+
`analise-003` (lista ICSAP em CID-9 derivada), `plan-002` (DuckDB Node Neo),
|
|
96
|
+
`plan-003` (rebuild automático, hoje no healthbr-data), `tool-specifications.md`.
|
|
97
|
+
|
|
98
|
+
## Licença
|
|
99
|
+
|
|
100
|
+
MIT (`LICENSE`). Os dados são do Ministério da Saúde / DATASUS; a redistribuição em
|
|
101
|
+
Parquet e os cubos derivados são do healthbr-data (CC-BY-4.0).
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export declare const CUBES_BASE_URL: string;
|
|
2
|
+
export declare const CUBES_CACHE_ENABLED: boolean;
|
|
3
|
+
export type CubeKind = "causas" | "series" | "icsap";
|
|
4
|
+
export interface CubesManifestFile {
|
|
5
|
+
name: string;
|
|
6
|
+
size_bytes: number;
|
|
7
|
+
sha256: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CubesManifestYear {
|
|
10
|
+
built_at: string | null;
|
|
11
|
+
builder_version: string | null;
|
|
12
|
+
records_in_cube: number | null;
|
|
13
|
+
window_complete: boolean | null;
|
|
14
|
+
ufs: number | null;
|
|
15
|
+
manifest_last_updated: string | null;
|
|
16
|
+
files: Record<CubeKind | "provenance", CubesManifestFile>;
|
|
17
|
+
}
|
|
18
|
+
export type PopulationKind = "pop_uf" | "pop_uf_agregado" | "pop_municipios";
|
|
19
|
+
/**
|
|
20
|
+
* >= 1.2.0 (2026-09-08, sih:populacao-no-canal): os denominadores populacionais
|
|
21
|
+
* publicados ao lado dos cubos pelo build-sih-population.yml do healthbr-data
|
|
22
|
+
* (build-population.R): pop_uf (IBGE, Projeção 2024, UF × sexo × idade simples,
|
|
23
|
+
* 2000..último cubo FECHADO), pop_municipios (DATASUS POPBR/POPSVS 1991–2024),
|
|
24
|
+
* pop_uf_agregado (soma por UF, 1991–1999) e o sidecar pop_provenance.json.
|
|
25
|
+
*/
|
|
26
|
+
export interface CubesManifestPopulation {
|
|
27
|
+
built_at: string | null;
|
|
28
|
+
builder_version: string | null;
|
|
29
|
+
/** Último ano de pop_uf = último cubo FECHADO do SIH (regra do CONTEXT.md). */
|
|
30
|
+
last_year: number | null;
|
|
31
|
+
rule?: string | null;
|
|
32
|
+
sources?: Array<{
|
|
33
|
+
file: string;
|
|
34
|
+
name: string;
|
|
35
|
+
agency?: string | null;
|
|
36
|
+
url?: string | null;
|
|
37
|
+
years?: string | null;
|
|
38
|
+
}>;
|
|
39
|
+
files: Record<PopulationKind | "provenance", CubesManifestFile>;
|
|
40
|
+
}
|
|
41
|
+
export interface CubesManifest {
|
|
42
|
+
manifest_version: string;
|
|
43
|
+
dataset: string;
|
|
44
|
+
generated_at: string;
|
|
45
|
+
base_url: string;
|
|
46
|
+
/** >= 1.1.0 (2026-09-08): quem gerou — repositório e pipeline do produtor. */
|
|
47
|
+
producer?: {
|
|
48
|
+
repository?: string;
|
|
49
|
+
pipeline?: string;
|
|
50
|
+
workflow?: string | null;
|
|
51
|
+
run_url?: string | null;
|
|
52
|
+
};
|
|
53
|
+
/** >= 1.1.0: tabelas de classificação publicadas em tables/, com SHA-256 — src/data/ é cópia (scripts/tables-check.mjs). */
|
|
54
|
+
tables?: Record<string, CubesManifestFile>;
|
|
55
|
+
/** >= 1.2.0: denominadores populacionais; null/ausente em manifesto anterior à população (não é erro do canal). */
|
|
56
|
+
population?: CubesManifestPopulation | null;
|
|
57
|
+
years: Record<string, CubesManifestYear>;
|
|
58
|
+
}
|
|
59
|
+
export declare function cubesCacheDir(): string;
|
|
60
|
+
/** Anos com os três cubos presentes numa pasta. */
|
|
61
|
+
export declare function yearsPresent(dir: string): number[];
|
|
62
|
+
/** Os três arquivos de população presentes numa pasta (o sidecar é opcional). */
|
|
63
|
+
export declare function populationPresent(dir: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Manifesto do canal: remoto (timeout curto), senão a cópia em disco da última
|
|
66
|
+
* vez, senão null. Memoizado por 10 min. Nunca lança.
|
|
67
|
+
*/
|
|
68
|
+
export declare function loadCubesManifest(opts?: {
|
|
69
|
+
timeoutMs?: number;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
manifest: CubesManifest | null;
|
|
72
|
+
source: "remote" | "disk" | "none";
|
|
73
|
+
}>;
|
|
74
|
+
export declare function publishedYears(manifest: CubesManifest | null): number[];
|
|
75
|
+
/** Só para testes: injeta um manifesto e zera a memoização. */
|
|
76
|
+
export declare function setCubesManifestForTests(manifest: CubesManifest | null): void;
|
|
77
|
+
/**
|
|
78
|
+
* O manifesto já carregado nesta execução (memoizado), sem ir à rede — para
|
|
79
|
+
* quem é síncrono, como populationProvenance(): depois de ensurePopulation()
|
|
80
|
+
* o memo está cheio; com o cache desligado (fixture, golden) fica null.
|
|
81
|
+
*/
|
|
82
|
+
export declare function cachedCubesManifest(): CubesManifest | null;
|
|
83
|
+
/** Um ano completo = três cubos + sidecar, todos verificados. Idempotente e sem corrida. */
|
|
84
|
+
export declare function ensureYear(dir: string, year: number, manifest: CubesManifest, log?: (msg: string) => void): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Garante os anos pedidos na pasta; `null` = todos os anos publicados (chamada
|
|
87
|
+
* sem ano explícito varre a série inteira). Devolve o que baixou e o que não
|
|
88
|
+
* existe no canal. Falha de rede num ano lança — o handler decide a mensagem.
|
|
89
|
+
*/
|
|
90
|
+
export declare function ensureYears(dir: string, years: number[] | null, log?: (msg: string) => void): Promise<{
|
|
91
|
+
downloaded: number[];
|
|
92
|
+
unavailable: number[];
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Garante os denominadores populacionais (três Parquet + pop_provenance.json)
|
|
96
|
+
* na pasta, baixados do canal e verificados como um ano de cubos. Idempotente
|
|
97
|
+
* (arquivo presente com o tamanho do manifesto não baixa) e sem corrida.
|
|
98
|
+
* `available = false` quando o manifesto do canal não tem o bloco `population`
|
|
99
|
+
* (manifesto anterior a 1.2.0) — o handler responde "sem denominador", não
|
|
100
|
+
* "erro do canal". Falha de rede lança — o handler decide a mensagem.
|
|
101
|
+
*/
|
|
102
|
+
export declare function ensurePopulation(dir: string, log?: (msg: string) => void): Promise<{
|
|
103
|
+
downloaded: string[];
|
|
104
|
+
available: boolean;
|
|
105
|
+
}>;
|
|
106
|
+
/** Anos que uma chamada de ferramenta pede, lidos dos argumentos; null = não diz. */
|
|
107
|
+
export declare function yearsFromArgs(args: unknown): number[] | null;
|
|
108
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache local dos cubos — item (c) de `sih:cubos-frescor` (2026-09-06).
|
|
3
|
+
*
|
|
4
|
+
* Os cubos anuais (`sih_{causas,series,icsap}_<ano>.parquet` + sidecar) são
|
|
5
|
+
* publicados pela pipeline sih-cubos do healthbr-data (rebuild-sih-cubes.yml;
|
|
6
|
+
* até 08/09/2026 o produtor era o rebuild-cubes.yml deste repositório) em
|
|
7
|
+
* `sih/cubos/` do bucket, servido em https://data.sidneybissoli.com/sih/cubos/
|
|
8
|
+
* com um `manifest.json` (tamanho e SHA-256 por arquivo e por tabela de
|
|
9
|
+
* classificação; healthbr-data scripts/pipeline/sih-cubos/cubes-manifest.mjs). O pacote npm
|
|
10
|
+
* NÃO embarca cubo nenhum (~60 MB por ano): quando a pasta de dados do
|
|
11
|
+
* projeto não tem cubos, o servidor usa `~/.cache/sih-br-mcp/cubos/` e baixa
|
|
12
|
+
* de lá só os anos que a chamada pede, verificando SHA-256 antes de aceitar.
|
|
13
|
+
*
|
|
14
|
+
* Variáveis de ambiente:
|
|
15
|
+
* SIH_CUBES_CACHE=off desliga tudo (smoke e golden usam: o baseline não
|
|
16
|
+
* pode depender de rede — ver docs/plan-003).
|
|
17
|
+
* SIH_CACHE_DIR=<pasta> onde guardar (padrão ~/.cache/sih-br-mcp/cubos).
|
|
18
|
+
* SIH_CUBES_BASE_URL=<url> outro canal (espelho, teste local).
|
|
19
|
+
*
|
|
20
|
+
* O que NÃO faz: não apaga nada, não atualiza cubo já presente (quem decide
|
|
21
|
+
* que um cubo está atrás é o frescor, `src/freshness.ts`; a reposição é
|
|
22
|
+
* apagar o arquivo do cache e chamar de novo).
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import { createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join, resolve } from "node:path";
|
|
28
|
+
import { Readable } from "node:stream";
|
|
29
|
+
import { pipeline } from "node:stream/promises";
|
|
30
|
+
export const CUBES_BASE_URL = (process.env.SIH_CUBES_BASE_URL ?? "https://data.sidneybissoli.com/sih/cubos/").replace(/\/?$/, "/");
|
|
31
|
+
export const CUBES_CACHE_ENABLED = process.env.SIH_CUBES_CACHE !== "off";
|
|
32
|
+
const CUBE_KINDS = ["causas", "series", "icsap"];
|
|
33
|
+
const POPULATION_KINDS = ["pop_uf", "pop_uf_agregado", "pop_municipios"];
|
|
34
|
+
export function cubesCacheDir() {
|
|
35
|
+
return process.env.SIH_CACHE_DIR ? resolve(process.env.SIH_CACHE_DIR) : join(homedir(), ".cache", "sih-br-mcp", "cubos");
|
|
36
|
+
}
|
|
37
|
+
/** Anos com os três cubos presentes numa pasta. */
|
|
38
|
+
export function yearsPresent(dir) {
|
|
39
|
+
if (!existsSync(dir))
|
|
40
|
+
return [];
|
|
41
|
+
const years = new Set();
|
|
42
|
+
for (let y = 1990; y <= 2100; y++) {
|
|
43
|
+
if (CUBE_KINDS.every((k) => existsSync(join(dir, `sih_${k}_${y}.parquet`))))
|
|
44
|
+
years.add(y);
|
|
45
|
+
}
|
|
46
|
+
return [...years].sort((a, b) => a - b);
|
|
47
|
+
}
|
|
48
|
+
/** Os três arquivos de população presentes numa pasta (o sidecar é opcional). */
|
|
49
|
+
export function populationPresent(dir) {
|
|
50
|
+
return existsSync(dir) && POPULATION_KINDS.every((k) => existsSync(join(dir, `${k}.parquet`)));
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Manifesto
|
|
54
|
+
const MANIFEST_TTL_MS = 10 * 60 * 1000;
|
|
55
|
+
let manifestMemo = null;
|
|
56
|
+
function parseManifest(text) {
|
|
57
|
+
try {
|
|
58
|
+
const m = JSON.parse(text);
|
|
59
|
+
if (!m || typeof m !== "object" || typeof m.years !== "object")
|
|
60
|
+
return null;
|
|
61
|
+
return m;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Manifesto do canal: remoto (timeout curto), senão a cópia em disco da última
|
|
69
|
+
* vez, senão null. Memoizado por 10 min. Nunca lança.
|
|
70
|
+
*/
|
|
71
|
+
export async function loadCubesManifest(opts = {}) {
|
|
72
|
+
if (manifestMemo && Date.now() - manifestMemo.at < MANIFEST_TTL_MS)
|
|
73
|
+
return manifestMemo;
|
|
74
|
+
const diskPath = join(cubesCacheDir(), "manifest.json");
|
|
75
|
+
let manifest = null;
|
|
76
|
+
let source = "none";
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(CUBES_BASE_URL + "manifest.json", { signal: AbortSignal.timeout(opts.timeoutMs ?? 8000) });
|
|
79
|
+
if (res.ok) {
|
|
80
|
+
const text = await res.text();
|
|
81
|
+
manifest = parseManifest(text);
|
|
82
|
+
if (manifest) {
|
|
83
|
+
source = "remote";
|
|
84
|
+
try {
|
|
85
|
+
mkdirSync(cubesCacheDir(), { recursive: true });
|
|
86
|
+
writeFileSync(diskPath, text);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* cache em disco é conveniência */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
/* rede indisponível: cai para o disco */
|
|
96
|
+
}
|
|
97
|
+
if (!manifest && existsSync(diskPath)) {
|
|
98
|
+
manifest = parseManifest(readFileSync(diskPath, "utf8"));
|
|
99
|
+
if (manifest)
|
|
100
|
+
source = "disk";
|
|
101
|
+
}
|
|
102
|
+
manifestMemo = { at: Date.now(), manifest, source };
|
|
103
|
+
return manifestMemo;
|
|
104
|
+
}
|
|
105
|
+
export function publishedYears(manifest) {
|
|
106
|
+
if (!manifest)
|
|
107
|
+
return [];
|
|
108
|
+
return Object.keys(manifest.years).map(Number).filter((y) => Number.isFinite(y)).sort((a, b) => a - b);
|
|
109
|
+
}
|
|
110
|
+
/** Só para testes: injeta um manifesto e zera a memoização. */
|
|
111
|
+
export function setCubesManifestForTests(manifest) {
|
|
112
|
+
manifestMemo = manifest ? { at: Date.now(), manifest, source: "remote" } : null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* O manifesto já carregado nesta execução (memoizado), sem ir à rede — para
|
|
116
|
+
* quem é síncrono, como populationProvenance(): depois de ensurePopulation()
|
|
117
|
+
* o memo está cheio; com o cache desligado (fixture, golden) fica null.
|
|
118
|
+
*/
|
|
119
|
+
export function cachedCubesManifest() {
|
|
120
|
+
return manifestMemo?.manifest ?? null;
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Download com verificação
|
|
124
|
+
async function sha256File(path) {
|
|
125
|
+
const { createReadStream } = await import("node:fs");
|
|
126
|
+
return new Promise((res, rej) => {
|
|
127
|
+
const h = createHash("sha256");
|
|
128
|
+
createReadStream(path).on("data", (d) => h.update(d)).on("end", () => res(h.digest("hex"))).on("error", rej);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async function downloadVerified(file, dir, timeoutMs) {
|
|
132
|
+
// ?v=<sha256>: os objetos do canal são reescritos NO LUGAR a cada rebuild e a
|
|
133
|
+
// borda do domínio guarda cópia pelo Cache-Control do objeto — sem isto, um
|
|
134
|
+
// consumidor recebia o cubo do build anterior (HIT, Age 15 h) enquanto o
|
|
135
|
+
// manifesto já assinava o novo, e a verificação abaixo falhava (2026-09-08,
|
|
136
|
+
// sih_series_2023: 36.047 bytes servidos vs 36.352 no manifesto). A query
|
|
137
|
+
// entra na chave de cache: versão nova = URL nova = nunca a cópia velha.
|
|
138
|
+
const url = `${CUBES_BASE_URL}${file.name}?v=${file.sha256.slice(0, 16)}`;
|
|
139
|
+
const final = join(dir, file.name);
|
|
140
|
+
const tmp = `${final}.part-${process.pid}`;
|
|
141
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
142
|
+
if (!res.ok || !res.body)
|
|
143
|
+
throw new Error(`${url}: HTTP ${res.status}`);
|
|
144
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(tmp));
|
|
145
|
+
const size = statSync(tmp).size;
|
|
146
|
+
if (size !== file.size_bytes) {
|
|
147
|
+
unlinkSync(tmp);
|
|
148
|
+
throw new Error(`${file.name}: ${size} bytes recebidos, manifesto diz ${file.size_bytes}`);
|
|
149
|
+
}
|
|
150
|
+
const digest = await sha256File(tmp);
|
|
151
|
+
if (digest !== file.sha256) {
|
|
152
|
+
unlinkSync(tmp);
|
|
153
|
+
throw new Error(`${file.name}: SHA-256 não confere com o manifesto`);
|
|
154
|
+
}
|
|
155
|
+
renameSync(tmp, final);
|
|
156
|
+
}
|
|
157
|
+
const inFlight = new Map();
|
|
158
|
+
/** Um ano completo = três cubos + sidecar, todos verificados. Idempotente e sem corrida. */
|
|
159
|
+
export function ensureYear(dir, year, manifest, log = () => { }) {
|
|
160
|
+
const entry = manifest.years[String(year)];
|
|
161
|
+
if (!entry)
|
|
162
|
+
return Promise.reject(new Error(`ano ${year} não está no canal ${CUBES_BASE_URL}`));
|
|
163
|
+
const have = inFlight.get(year);
|
|
164
|
+
if (have)
|
|
165
|
+
return have;
|
|
166
|
+
const p = (async () => {
|
|
167
|
+
mkdirSync(dir, { recursive: true });
|
|
168
|
+
const wanted = [...CUBE_KINDS.map((k) => entry.files[k]), entry.files.provenance].filter(Boolean);
|
|
169
|
+
for (const f of wanted) {
|
|
170
|
+
const path = join(dir, f.name);
|
|
171
|
+
if (existsSync(path) && statSync(path).size === f.size_bytes)
|
|
172
|
+
continue;
|
|
173
|
+
const t0 = Date.now();
|
|
174
|
+
log(`baixando ${f.name} (${(f.size_bytes / 1e6).toFixed(1)} MB) de ${CUBES_BASE_URL}`);
|
|
175
|
+
await downloadVerified(f, dir, 10 * 60 * 1000);
|
|
176
|
+
log(` ${f.name} ok em ${((Date.now() - t0) / 1000).toFixed(1)} s (SHA-256 confere)`);
|
|
177
|
+
}
|
|
178
|
+
})().finally(() => inFlight.delete(year));
|
|
179
|
+
inFlight.set(year, p);
|
|
180
|
+
return p;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Garante os anos pedidos na pasta; `null` = todos os anos publicados (chamada
|
|
184
|
+
* sem ano explícito varre a série inteira). Devolve o que baixou e o que não
|
|
185
|
+
* existe no canal. Falha de rede num ano lança — o handler decide a mensagem.
|
|
186
|
+
*/
|
|
187
|
+
export async function ensureYears(dir, years, log) {
|
|
188
|
+
const { manifest } = await loadCubesManifest();
|
|
189
|
+
if (!manifest)
|
|
190
|
+
throw new Error(`manifesto dos cubos indisponível (${CUBES_BASE_URL}manifest.json) e sem cópia local`);
|
|
191
|
+
const present = new Set(yearsPresent(dir));
|
|
192
|
+
const wanted = (years ?? publishedYears(manifest)).filter((y) => !present.has(y));
|
|
193
|
+
const downloaded = [];
|
|
194
|
+
const unavailable = [];
|
|
195
|
+
for (const y of wanted) {
|
|
196
|
+
if (!manifest.years[String(y)]) {
|
|
197
|
+
unavailable.push(y);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
await ensureYear(dir, y, manifest, log);
|
|
201
|
+
downloaded.push(y);
|
|
202
|
+
}
|
|
203
|
+
return { downloaded, unavailable };
|
|
204
|
+
}
|
|
205
|
+
let populationInFlight = null;
|
|
206
|
+
/**
|
|
207
|
+
* Garante os denominadores populacionais (três Parquet + pop_provenance.json)
|
|
208
|
+
* na pasta, baixados do canal e verificados como um ano de cubos. Idempotente
|
|
209
|
+
* (arquivo presente com o tamanho do manifesto não baixa) e sem corrida.
|
|
210
|
+
* `available = false` quando o manifesto do canal não tem o bloco `population`
|
|
211
|
+
* (manifesto anterior a 1.2.0) — o handler responde "sem denominador", não
|
|
212
|
+
* "erro do canal". Falha de rede lança — o handler decide a mensagem.
|
|
213
|
+
*/
|
|
214
|
+
export function ensurePopulation(dir, log = () => { }) {
|
|
215
|
+
if (populationInFlight)
|
|
216
|
+
return populationInFlight;
|
|
217
|
+
const p = (async () => {
|
|
218
|
+
const { manifest } = await loadCubesManifest();
|
|
219
|
+
if (!manifest)
|
|
220
|
+
throw new Error(`manifesto dos cubos indisponível (${CUBES_BASE_URL}manifest.json) e sem cópia local`);
|
|
221
|
+
const pop = manifest.population;
|
|
222
|
+
if (!pop || !pop.files)
|
|
223
|
+
return { downloaded: [], available: false };
|
|
224
|
+
mkdirSync(dir, { recursive: true });
|
|
225
|
+
const wanted = [...POPULATION_KINDS.map((k) => pop.files[k]), pop.files.provenance].filter(Boolean);
|
|
226
|
+
const downloaded = [];
|
|
227
|
+
for (const f of wanted) {
|
|
228
|
+
const path = join(dir, f.name);
|
|
229
|
+
if (existsSync(path) && statSync(path).size === f.size_bytes)
|
|
230
|
+
continue;
|
|
231
|
+
const t0 = Date.now();
|
|
232
|
+
log(`baixando ${f.name} (${(f.size_bytes / 1e6).toFixed(1)} MB) de ${CUBES_BASE_URL}`);
|
|
233
|
+
await downloadVerified(f, dir, 10 * 60 * 1000);
|
|
234
|
+
log(` ${f.name} ok em ${((Date.now() - t0) / 1000).toFixed(1)} s (SHA-256 confere)`);
|
|
235
|
+
downloaded.push(f.name);
|
|
236
|
+
}
|
|
237
|
+
return { downloaded, available: true };
|
|
238
|
+
})().finally(() => {
|
|
239
|
+
populationInFlight = null;
|
|
240
|
+
});
|
|
241
|
+
populationInFlight = p;
|
|
242
|
+
return p;
|
|
243
|
+
}
|
|
244
|
+
/** Anos que uma chamada de ferramenta pede, lidos dos argumentos; null = não diz. */
|
|
245
|
+
export function yearsFromArgs(args) {
|
|
246
|
+
if (!args || typeof args !== "object")
|
|
247
|
+
return null;
|
|
248
|
+
const a = args;
|
|
249
|
+
const out = new Set();
|
|
250
|
+
const push = (v) => {
|
|
251
|
+
if (Array.isArray(v))
|
|
252
|
+
v.forEach(push);
|
|
253
|
+
else if (typeof v === "number" && Number.isFinite(v))
|
|
254
|
+
out.add(v);
|
|
255
|
+
else if (typeof v === "string" && /^\d{4}$/.test(v))
|
|
256
|
+
out.add(Number(v));
|
|
257
|
+
};
|
|
258
|
+
push(a.year);
|
|
259
|
+
push(a.years);
|
|
260
|
+
if (typeof a.start_year === "number" && typeof a.end_year === "number" && a.end_year >= a.start_year) {
|
|
261
|
+
for (let y = a.start_year; y <= a.end_year; y++)
|
|
262
|
+
out.add(y);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
push(a.start_year);
|
|
266
|
+
push(a.end_year);
|
|
267
|
+
}
|
|
268
|
+
return out.size ? [...out].sort((x, y) => x - y) : null;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"metadata": {
|
|
3
|
+
"source": "IBGE",
|
|
4
|
+
"total_regions": 5,
|
|
5
|
+
"total_ufs": 27
|
|
6
|
+
},
|
|
7
|
+
"regions": [
|
|
8
|
+
{
|
|
9
|
+
"code": "N",
|
|
10
|
+
"name_pt": "Norte",
|
|
11
|
+
"name_en": "North",
|
|
12
|
+
"ufs": ["AC", "AM", "AP", "PA", "RO", "RR", "TO"]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"code": "NE",
|
|
16
|
+
"name_pt": "Nordeste",
|
|
17
|
+
"name_en": "Northeast",
|
|
18
|
+
"ufs": ["AL", "BA", "CE", "MA", "PB", "PE", "PI", "RN", "SE"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"code": "SE",
|
|
22
|
+
"name_pt": "Sudeste",
|
|
23
|
+
"name_en": "Southeast",
|
|
24
|
+
"ufs": ["ES", "MG", "RJ", "SP"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"code": "S",
|
|
28
|
+
"name_pt": "Sul",
|
|
29
|
+
"name_en": "South",
|
|
30
|
+
"ufs": ["PR", "RS", "SC"]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"code": "CO",
|
|
34
|
+
"name_pt": "Centro-Oeste",
|
|
35
|
+
"name_en": "Central-West",
|
|
36
|
+
"ufs": ["DF", "GO", "MS", "MT"]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"ufs": [
|
|
40
|
+
{ "code": "AC", "ibge_code": "12", "name": "Acre", "region": "N", "capital": "Rio Branco" },
|
|
41
|
+
{ "code": "AL", "ibge_code": "27", "name": "Alagoas", "region": "NE", "capital": "Maceió" },
|
|
42
|
+
{ "code": "AM", "ibge_code": "13", "name": "Amazonas", "region": "N", "capital": "Manaus" },
|
|
43
|
+
{ "code": "AP", "ibge_code": "16", "name": "Amapá", "region": "N", "capital": "Macapá" },
|
|
44
|
+
{ "code": "BA", "ibge_code": "29", "name": "Bahia", "region": "NE", "capital": "Salvador" },
|
|
45
|
+
{ "code": "CE", "ibge_code": "23", "name": "Ceará", "region": "NE", "capital": "Fortaleza" },
|
|
46
|
+
{ "code": "DF", "ibge_code": "53", "name": "Distrito Federal", "region": "CO", "capital": "Brasília" },
|
|
47
|
+
{ "code": "ES", "ibge_code": "32", "name": "Espírito Santo", "region": "SE", "capital": "Vitória" },
|
|
48
|
+
{ "code": "GO", "ibge_code": "52", "name": "Goiás", "region": "CO", "capital": "Goiânia" },
|
|
49
|
+
{ "code": "MA", "ibge_code": "21", "name": "Maranhão", "region": "NE", "capital": "São Luís" },
|
|
50
|
+
{ "code": "MG", "ibge_code": "31", "name": "Minas Gerais", "region": "SE", "capital": "Belo Horizonte" },
|
|
51
|
+
{ "code": "MS", "ibge_code": "50", "name": "Mato Grosso do Sul", "region": "CO", "capital": "Campo Grande" },
|
|
52
|
+
{ "code": "MT", "ibge_code": "51", "name": "Mato Grosso", "region": "CO", "capital": "Cuiabá" },
|
|
53
|
+
{ "code": "PA", "ibge_code": "15", "name": "Pará", "region": "N", "capital": "Belém" },
|
|
54
|
+
{ "code": "PB", "ibge_code": "25", "name": "Paraíba", "region": "NE", "capital": "João Pessoa" },
|
|
55
|
+
{ "code": "PE", "ibge_code": "26", "name": "Pernambuco", "region": "NE", "capital": "Recife" },
|
|
56
|
+
{ "code": "PI", "ibge_code": "22", "name": "Piauí", "region": "NE", "capital": "Teresina" },
|
|
57
|
+
{ "code": "PR", "ibge_code": "41", "name": "Paraná", "region": "S", "capital": "Curitiba" },
|
|
58
|
+
{ "code": "RJ", "ibge_code": "33", "name": "Rio de Janeiro", "region": "SE", "capital": "Rio de Janeiro" },
|
|
59
|
+
{ "code": "RN", "ibge_code": "24", "name": "Rio Grande do Norte", "region": "NE", "capital": "Natal" },
|
|
60
|
+
{ "code": "RO", "ibge_code": "11", "name": "Rondônia", "region": "N", "capital": "Porto Velho" },
|
|
61
|
+
{ "code": "RR", "ibge_code": "14", "name": "Roraima", "region": "N", "capital": "Boa Vista" },
|
|
62
|
+
{ "code": "RS", "ibge_code": "43", "name": "Rio Grande do Sul", "region": "S", "capital": "Porto Alegre" },
|
|
63
|
+
{ "code": "SC", "ibge_code": "42", "name": "Santa Catarina", "region": "S", "capital": "Florianópolis" },
|
|
64
|
+
{ "code": "SE", "ibge_code": "28", "name": "Sergipe", "region": "NE", "capital": "Aracaju" },
|
|
65
|
+
{ "code": "SP", "ibge_code": "35", "name": "São Paulo", "region": "SE", "capital": "São Paulo" },
|
|
66
|
+
{ "code": "TO", "ibge_code": "17", "name": "Tocantins", "region": "N", "capital": "Palmas" }
|
|
67
|
+
]
|
|
68
|
+
}
|