@mcpassure/mcp-anvisa-bulario 0.1.0 → 2.1.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.
Files changed (47) hide show
  1. package/README.en.md +34 -8
  2. package/README.md +34 -8
  3. package/dist/bootstrap.d.ts +11 -0
  4. package/dist/bootstrap.d.ts.map +1 -0
  5. package/dist/bootstrap.js +120 -0
  6. package/dist/bootstrap.js.map +1 -0
  7. package/dist/config.d.ts +10 -7
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +11 -12
  10. package/dist/config.js.map +1 -1
  11. package/dist/db/dataset.d.ts +15 -0
  12. package/dist/db/dataset.d.ts.map +1 -0
  13. package/dist/db/dataset.js +168 -0
  14. package/dist/db/dataset.js.map +1 -0
  15. package/dist/db/schema.d.ts +11 -0
  16. package/dist/db/schema.d.ts.map +1 -0
  17. package/dist/db/schema.js +48 -0
  18. package/dist/db/schema.js.map +1 -0
  19. package/dist/domain/repository.d.ts +11 -5
  20. package/dist/domain/repository.d.ts.map +1 -1
  21. package/dist/domain/repository.js +38 -58
  22. package/dist/domain/repository.js.map +1 -1
  23. package/dist/index.js +15 -14
  24. package/dist/index.js.map +1 -1
  25. package/dist/schemas/tools.d.ts +4 -4
  26. package/dist/server.js +1 -1
  27. package/dist/tools/consultar-bula.d.ts +10 -0
  28. package/dist/tools/consultar-bula.d.ts.map +1 -1
  29. package/dist/tools/consultar-bula.js +41 -23
  30. package/dist/tools/consultar-bula.js.map +1 -1
  31. package/dist/tools/listar-apresentacoes.d.ts +7 -0
  32. package/dist/tools/listar-apresentacoes.d.ts.map +1 -1
  33. package/dist/tools/listar-apresentacoes.js +26 -8
  34. package/dist/tools/listar-apresentacoes.js.map +1 -1
  35. package/dist/tools/shared.d.ts +20 -11
  36. package/dist/tools/shared.d.ts.map +1 -1
  37. package/dist/tools/shared.js +50 -10
  38. package/dist/tools/shared.js.map +1 -1
  39. package/dist/utils/http.d.ts +12 -33
  40. package/dist/utils/http.d.ts.map +1 -1
  41. package/dist/utils/http.js +15 -167
  42. package/dist/utils/http.js.map +1 -1
  43. package/package.json +25 -23
  44. package/dist/utils/playwright-http.d.ts +0 -2
  45. package/dist/utils/playwright-http.d.ts.map +0 -1
  46. package/dist/utils/playwright-http.js +0 -4
  47. package/dist/utils/playwright-http.js.map +0 -1
package/README.en.md CHANGED
@@ -8,9 +8,10 @@
8
8
 
9
9
  Enables AI agents (Claude, GPT, Copilot) to query drug leaflets (bulas), active ingredients, therapeutic classes, and other metadata for medications registered in Brazil, with structured responses and efficient caching.
10
10
 
11
- [![CI](https://github.com/mcpassure/mcp-anvisa-bulario/actions/workflows/ci.yml/badge.svg)](https://github.com/mcpassure/mcp-anvisa-bulario/actions)
12
11
  [![npm](https://img.shields.io/npm/v/@mcpassure/mcp-anvisa-bulario)](https://www.npmjs.com/package/@mcpassure/mcp-anvisa-bulario)
13
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
13
+ [![OSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/mcpassure/monorepo/badge)](https://securityscorecards.dev/viewer/?uri=github.com/mcpassure/monorepo)
14
+ [![SAFE-MCP](https://img.shields.io/badge/SAFE--MCP-mapped-green)](#safe-mcp-mapping)
14
15
 
15
16
  ---
16
17
 
@@ -89,13 +90,7 @@ Add to `claude_desktop_config.json`:
89
90
 
90
91
  ## Demo
91
92
 
92
- ![Demo](./.github/assets/demo.gif)
93
-
94
- To run locally:
95
-
96
- ```bash
97
- npm run demo
98
- ```
93
+ 🚧 Demo GIF coming soon.
99
94
 
100
95
  ---
101
96
 
@@ -194,3 +189,34 @@ Maintained by [MCPAssure Brasil](https://github.com/mcpassure). See [CONTRIBUTIN
194
189
  ---
195
190
 
196
191
  Part of the [MCPAssure](https://github.com/mcpassure) catalog — curated MCP examples for Brazilian healthcare.
192
+
193
+ ---
194
+
195
+ ## SAFE-MCP Mapping
196
+
197
+ Assessment against the SAFE-MCP framework (OpenSSF + LF + OpenID Foundation).
198
+
199
+ ### Mitigated attacks
200
+
201
+ | ID | Attack | Status | How we mitigate it |
202
+ |----|--------|--------|-------------------|
203
+ | SAFE-T001 | Tool Poisoning | ✓ Mitigated | Strict Zod schema; drug names validated before querying the database |
204
+ | SAFE-T002 | Indirect Prompt Injection | ✓ Mitigated | Structured output (`structuredContent`); ANVISA content is tabular, no executable markup |
205
+ | SAFE-T003 | Credential exposure | ✓ Mitigated | Public ANVISA data with no authentication; R2 only in server-side Worker |
206
+ | SAFE-T004 | Data exfiltration | ✓ Mitigated | Read-only on public drug data; no access to user data |
207
+ | SAFE-T005 | Resource exhaustion | ✓ Mitigated | Local SQLite cache (18MB); canary uses HEAD request or single header download |
208
+
209
+ ### NOT mitigated (honest declaration)
210
+
211
+ | ID | Attack | Why not mitigated |
212
+ |----|--------|------------------|
213
+ | SAFE-T010 | Supply-chain attack via npm | `pnpm audit` + Renovate; no SBOM generated yet |
214
+ | SAFE-T015 | Side-channel timing analysis | Not relevant for public tabular drug data |
215
+
216
+ ### Lethal Trifecta Declaration (Willison, 2025)
217
+
218
+ 1. **Private data access** — ✗ **Absent.** ANVISA Bulário is entirely public; no patient data is accessed.
219
+ 2. **Untrusted content exposure** — ⚠ **Partial.** Inputs validated by Zod; ANVISA data is governmental.
220
+ 3. **External communication capability** — ✗ **Absent.** MCP operates on local SQLite cache; no runtime external calls.
221
+
222
+ **Conclusion:** this MCP **does not combine all 3 factors simultaneously**. Local-first architecture eliminates the external communication factor.
package/README.md CHANGED
@@ -8,9 +8,10 @@
8
8
 
9
9
  Permite que agentes de IA (Claude, GPT, Copilot) consultem bulas, princípios ativos, classes terapêuticas e demais metadados de medicamentos registrados no Brasil com resposta estruturada e cache eficiente.
10
10
 
11
- [![CI](https://github.com/mcpassure/mcp-anvisa-bulario/actions/workflows/ci.yml/badge.svg)](https://github.com/mcpassure/mcp-anvisa-bulario/actions)
12
11
  [![npm](https://img.shields.io/npm/v/@mcpassure/mcp-anvisa-bulario)](https://www.npmjs.com/package/@mcpassure/mcp-anvisa-bulario)
13
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
13
+ [![OSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/mcpassure/monorepo/badge)](https://securityscorecards.dev/viewer/?uri=github.com/mcpassure/monorepo)
14
+ [![SAFE-MCP](https://img.shields.io/badge/SAFE--MCP-mapped-green)](#safe-mcp-mapping)
14
15
 
15
16
  ---
16
17
 
@@ -90,13 +91,7 @@ Adicione ao `claude_desktop_config.json`:
90
91
 
91
92
  ## Demo
92
93
 
93
- ![Demo](./.github/assets/demo.gif)
94
-
95
- Para rodar localmente:
96
-
97
- ```bash
98
- npm run demo
99
- ```
94
+ 🚧 Demo GIF em breve.
100
95
 
101
96
  ---
102
97
 
@@ -190,3 +185,34 @@ Mantido pela [MCPAssure Brasil](https://github.com/mcpassure). Consulte [CONTRIB
190
185
  ---
191
186
 
192
187
  Parte do catálogo [MCPAssure](https://github.com/mcpassure) — exemplos de curadoria de MCPs para saúde brasileira.
188
+
189
+ ---
190
+
191
+ ## SAFE-MCP Mapping
192
+
193
+ Avaliação contra o framework SAFE-MCP (OpenSSF + LF + OpenID Foundation).
194
+
195
+ ### Ataques mitigados
196
+
197
+ | ID | Ataque | Status | Como mitigamos |
198
+ |----|--------|--------|----------------|
199
+ | SAFE-T001 | Tool Poisoning | ✓ Mitigado | Schema Zod estrito; nomes de medicamentos validados antes de consultar banco |
200
+ | SAFE-T002 | Indirect Prompt Injection | ✓ Mitigado | Output estruturado (`structuredContent`); conteúdo ANVISA é tabular, sem markup executável |
201
+ | SAFE-T003 | Credential exposure | ✓ Mitigado | Dados públicos ANVISA sem autenticação; R2 apenas em Worker server-side |
202
+ | SAFE-T004 | Data exfiltration | ✓ Mitigado | Read-only sobre dados públicos de medicamentos; sem acesso a dados de usuário |
203
+ | SAFE-T005 | Resource exhaustion | ✓ Mitigado | Cache SQLite local (18MB); canary usa HEAD request ou download único de header |
204
+
205
+ ### Ataques NÃO mitigados (declaração honesta)
206
+
207
+ | ID | Ataque | Por que não mitigado |
208
+ |----|--------|---------------------|
209
+ | SAFE-T010 | Supply-chain attack via npm | `pnpm audit` + Renovate; sem SBOM gerado ainda |
210
+ | SAFE-T015 | Side-channel timing analysis | Não relevante para dados públicos tabelados de medicamentos |
211
+
212
+ ### Lethal Trifecta Declaration (Willison, 2025)
213
+
214
+ 1. **Acesso a dados privados** — ✗ **Ausente.** Bulário ANVISA é integralmente público; nenhum dado de paciente é acessado.
215
+ 2. **Exposição a conteúdo não-confiável** — ⚠ **Parcial.** Inputs validados por Zod; dados ANVISA são governamentais.
216
+ 3. **Capacidade de comunicação externa** — ✗ **Ausente.** MCP opera sobre banco SQLite local; sem chamadas em runtime.
217
+
218
+ **Conclusão:** este MCP **não combina os 3 fatores simultaneamente**. Arquitetura local-first elimina o fator de comunicação externa.
@@ -0,0 +1,11 @@
1
+ export declare function getDbPath(): string;
2
+ export type BootstrapResult = {
3
+ ok: true;
4
+ action: "downloaded" | "up_to_date" | "skipped_offline";
5
+ version: string;
6
+ } | {
7
+ ok: false;
8
+ reason: string;
9
+ };
10
+ export declare function ensureDataset(): Promise<BootstrapResult>;
11
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAoCA,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAuDD,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,YAAY,GAAG,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAsB,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC,CAwE9D"}
@@ -0,0 +1,120 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ import { AwsClient } from "aws4fetch";
7
+ const DATASET = "anvisa-bulario";
8
+ const BUCKET = "mcpassure-datasets";
9
+ const MANIFEST_KEY = `bulario/latest/manifest.json`;
10
+ function getDataDir() {
11
+ return process.platform === "win32"
12
+ ? join(process.env.APPDATA ?? homedir(), "mcpassure", DATASET)
13
+ : join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "mcpassure", DATASET);
14
+ }
15
+ export function getDbPath() {
16
+ return process.env.MCPASSURE_DB_PATH ?? join(getDataDir(), "bulario.db");
17
+ }
18
+ function getLocalManifestPath() {
19
+ return join(getDataDir(), "manifest.json");
20
+ }
21
+ function log(msg) {
22
+ process.stderr.write(`[bootstrap] ${msg}\n`);
23
+ }
24
+ async function fetchManifest(client, endpoint) {
25
+ const url = `${endpoint}/${BUCKET}/${MANIFEST_KEY}`;
26
+ const resp = await client.fetch(url);
27
+ if (!resp.ok) {
28
+ throw new Error(`HTTP ${resp.status} ao buscar manifest em ${url}`);
29
+ }
30
+ return (await resp.json());
31
+ }
32
+ async function downloadAndVerify(client, endpoint, manifest, targetPath) {
33
+ const url = `${endpoint}/${manifest.artifact.bucket}/${manifest.artifact.key}`;
34
+ const tmp = `${targetPath}.download`;
35
+ await mkdir(dirname(targetPath), { recursive: true });
36
+ log(`Baixando ${url} (${(manifest.artifact.size_bytes / 1024 / 1024).toFixed(1)} MB)...`);
37
+ const resp = await client.fetch(url);
38
+ if (!resp.ok) {
39
+ throw new Error(`HTTP ${resp.status} ao baixar artifact ${url}`);
40
+ }
41
+ const buf = Buffer.from(await resp.arrayBuffer());
42
+ if (buf.length !== manifest.artifact.size_bytes) {
43
+ throw new Error(`Tamanho inesperado. Esperado ${manifest.artifact.size_bytes}, recebido ${buf.length}.`);
44
+ }
45
+ const hash = createHash("sha256").update(buf).digest("hex");
46
+ if (hash.toLowerCase() !== manifest.artifact.sha256.toLowerCase()) {
47
+ throw new Error(`Checksum SHA-256 não bate. Esperado ${manifest.artifact.sha256}, recebido ${hash}.`);
48
+ }
49
+ await writeFile(tmp, buf);
50
+ await rename(tmp, targetPath);
51
+ log(`Download concluído. SHA-256 validado.`);
52
+ }
53
+ export async function ensureDataset() {
54
+ const dbPath = getDbPath();
55
+ const manifestPath = getLocalManifestPath();
56
+ const dbExists = existsSync(dbPath);
57
+ const accessKey = process.env.MCPASSURE_R2_ACCESS_KEY_ID;
58
+ const secretKey = process.env.MCPASSURE_R2_SECRET_ACCESS_KEY;
59
+ const endpoint = process.env.MCPASSURE_R2_ENDPOINT;
60
+ if (!accessKey || !secretKey || !endpoint) {
61
+ if (dbExists) {
62
+ log("Credenciais R2 ausentes — usando cache local existente.");
63
+ return { ok: true, action: "skipped_offline", version: "local" };
64
+ }
65
+ return {
66
+ ok: false,
67
+ reason: "Cache local inexistente e credenciais R2 ausentes. " +
68
+ "Configure MCPASSURE_R2_ACCESS_KEY_ID, MCPASSURE_R2_SECRET_ACCESS_KEY e MCPASSURE_R2_ENDPOINT, " +
69
+ "ou execute `npm run sync` para popular o dataset local.",
70
+ };
71
+ }
72
+ const client = new AwsClient({
73
+ accessKeyId: accessKey,
74
+ secretAccessKey: secretKey,
75
+ service: "s3",
76
+ region: "auto",
77
+ });
78
+ let remoteManifest;
79
+ try {
80
+ remoteManifest = await fetchManifest(client, endpoint);
81
+ }
82
+ catch (err) {
83
+ const reason = err instanceof Error ? err.message : String(err);
84
+ if (dbExists) {
85
+ log(`Falha ao buscar manifest remoto (${reason}). Usando cache local.`);
86
+ return { ok: true, action: "skipped_offline", version: "stale" };
87
+ }
88
+ return { ok: false, reason: `Sem cache e sem rede: ${reason}` };
89
+ }
90
+ if (dbExists && existsSync(manifestPath)) {
91
+ try {
92
+ const localManifest = JSON.parse(await readFile(manifestPath, "utf8"));
93
+ if (localManifest.version === remoteManifest.version) {
94
+ return { ok: true, action: "up_to_date", version: localManifest.version };
95
+ }
96
+ log(`Versão local (${localManifest.version}) difere de remota (${remoteManifest.version}). Atualizando.`);
97
+ }
98
+ catch {
99
+ log("Manifest local corrompido. Re-baixando.");
100
+ }
101
+ }
102
+ try {
103
+ await downloadAndVerify(client, endpoint, remoteManifest, dbPath);
104
+ await writeFile(manifestPath, JSON.stringify(remoteManifest, null, 2));
105
+ return { ok: true, action: "downloaded", version: remoteManifest.version };
106
+ }
107
+ catch (err) {
108
+ const reason = err instanceof Error ? err.message : String(err);
109
+ const tmp = `${dbPath}.download`;
110
+ if (existsSync(tmp)) {
111
+ await unlink(tmp).catch(() => { });
112
+ }
113
+ if (dbExists) {
114
+ log(`Falha no download (${reason}). Mantendo cache anterior.`);
115
+ return { ok: true, action: "skipped_offline", version: "stale" };
116
+ }
117
+ return { ok: false, reason };
118
+ }
119
+ }
120
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -1,12 +1,15 @@
1
+ /**
2
+ * config.ts — v2.0
3
+ *
4
+ * Configuração simplificada. O dataset agora é gerenciado pelo bootstrap.ts (R2)
5
+ * ou pelo script sync.ts (CSV direto da ANVISA).
6
+ *
7
+ * Paths migrados:
8
+ * ~/.local/share/mcpassure-anvisa/ → ~/.local/share/mcpassure/anvisa-bulario/
9
+ * %APPDATA%/mcpassure-anvisa/ → %APPDATA%/mcpassure/anvisa-bulario/
10
+ */
1
11
  export type Config = {
2
- cachePath: string;
3
- maxRetries: number;
4
- baseDelayMs: number;
5
- requestTimeoutMs: number;
6
- anvisaBaseUrl: string;
7
12
  degradedThresholdDays: number;
8
- statusPageEnabled: boolean;
9
- statusPageUrl: string;
10
13
  };
11
14
  export declare function loadConfig(): Config;
12
15
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,MAAM,GAAG;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAgB,UAAU,IAAI,MAAM,CAkBnC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,MAAM,GAAG;IACnB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,wBAAgB,UAAU,IAAI,MAAM,CAOnC"}
package/dist/config.js CHANGED
@@ -1,17 +1,16 @@
1
- import os from "node:os";
2
- import path from "node:path";
1
+ /**
2
+ * config.ts — v2.0
3
+ *
4
+ * Configuração simplificada. O dataset agora é gerenciado pelo bootstrap.ts (R2)
5
+ * ou pelo script sync.ts (CSV direto da ANVISA).
6
+ *
7
+ * Paths migrados:
8
+ * ~/.local/share/mcpassure-anvisa/ → ~/.local/share/mcpassure/anvisa-bulario/
9
+ * %APPDATA%/mcpassure-anvisa/ → %APPDATA%/mcpassure/anvisa-bulario/
10
+ */
3
11
  export function loadConfig() {
4
12
  return {
5
- cachePath: process.env.MCPASSURE_CACHE_PATH ??
6
- path.join(os.homedir(), ".cache", "mcpassure-anvisa", "cache.db"),
7
- maxRetries: Number.parseInt(process.env.MCPASSURE_MAX_RETRIES ?? "3", 10),
8
- baseDelayMs: Number.parseInt(process.env.MCPASSURE_BASE_DELAY_MS ?? "1000", 10),
9
- requestTimeoutMs: Number.parseInt(process.env.MCPASSURE_REQUEST_TIMEOUT_MS ?? "10000", 10),
10
- anvisaBaseUrl: process.env.MCPASSURE_ANVISA_BASE_URL ?? "https://consultas.anvisa.gov.br",
11
- degradedThresholdDays: Number.parseInt(process.env.MCPASSURE_DEGRADED_THRESHOLD_DAYS ?? "30", 10),
12
- statusPageEnabled: process.env.MCPASSURE_STATUS_HEARTBEAT === "true",
13
- statusPageUrl: process.env.MCPASSURE_STATUS_PAGE_URL ??
14
- "https://status.mcpassure.com.br/api/v1/heartbeat/anvisa-bulario",
13
+ degradedThresholdDays: Number.parseInt(process.env.MCPASSURE_DEGRADED_THRESHOLD_DAYS ?? "7", 10),
15
14
  };
16
15
  }
17
16
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,SAAS,EACP,OAAO,CAAC,GAAG,CAAC,oBAAoB;YAChC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,kBAAkB,EAAE,UAAU,CAAC;QACnE,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,GAAG,EAAE,EAAE,CAAC;QACzE,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,MAAM,EAAE,EAAE,CAAC;QAC/E,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,OAAO,EAAE,EAAE,CAAC;QAC1F,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,iCAAiC;QACzF,qBAAqB,EAAE,MAAM,CAAC,QAAQ,CACpC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,IAAI,EACrD,EAAE,CACH;QACD,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,MAAM;QACpE,aAAa,EACX,OAAO,CAAC,GAAG,CAAC,yBAAyB;YACrC,iEAAiE;KACpE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,qBAAqB,EAAE,MAAM,CAAC,QAAQ,CACpC,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,GAAG,EACpD,EAAE,CACH;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Apresentacao, BulaLink, BularioSource, MedicamentoDetalhes, MedicamentoResumo, SearchByClasseTerapeuticaParams, SearchByNameParams, SearchByPrincipalAtivoParams, SearchByTarjaParams } from "../sources/types.js";
2
+ export declare class BularioDatasetSource implements BularioSource {
3
+ readonly name = "anvisa_dados_abertos_sqlite";
4
+ private readonly db;
5
+ constructor(dbPath: string);
6
+ searchByName(params: SearchByNameParams): Promise<MedicamentoResumo[]>;
7
+ searchByPrincipalAtivo(params: SearchByPrincipalAtivoParams): Promise<MedicamentoResumo[]>;
8
+ searchByClasseTerapeutica(params: SearchByClasseTerapeuticaParams): Promise<MedicamentoResumo[]>;
9
+ searchByTarja(params: SearchByTarjaParams): Promise<MedicamentoResumo[]>;
10
+ getDetalhes(numProcesso: string): Promise<MedicamentoDetalhes>;
11
+ getApresentacoes(numProcesso: string): Promise<Apresentacao[]>;
12
+ getBulaLink(_idBulaProtegido: string): Promise<BulaLink>;
13
+ close(): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=dataset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../../src/db/dataset.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,+BAA+B,EAC/B,kBAAkB,EAClB,4BAA4B,EAC5B,mBAAmB,EAEpB,MAAM,qBAAqB,CAAC;AA+D7B,qBAAa,oBAAqB,YAAW,aAAa;IACxD,QAAQ,CAAC,IAAI,iCAAiC;IAC9C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,MAAM,EAAE,MAAM;IAK1B,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAgBtE,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAgB1F,yBAAyB,CAAC,MAAM,EAAE,+BAA+B,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAgBhG,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAwCxE,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAkB9D,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAiC9D,WAAW,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAKxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAIvB"}
@@ -0,0 +1,168 @@
1
+ import Database from "better-sqlite3";
2
+ // ── Tarja normalisation ───────────────────────────────────────────────────────
3
+ // v2.0: CATEGORIA_REGULATORIA no CSV não mapeia diretamente para LIVRE/VERMELHA/PRETA.
4
+ // As categorias reais do CSV são: "Similar", "Novo", "Genérico", "BAIXO RISCO", etc.
5
+ // O campo tarja (da Portaria 344/98) NÃO está disponível no CSV de dados abertos.
6
+ // Mapeamos heuristicamente pelo tipo de produto e categoria para fins de filtragem.
7
+ const TARJA_KEYWORDS = [
8
+ { pattern: /psicotr[oó]pic|entorpecente|controle especial/i, tarja: "PRETA" },
9
+ { pattern: /antimicrobiano|antibiótico|antibi[oó]tic/i, tarja: "VERMELHA" },
10
+ { pattern: /baixo risco|isento/i, tarja: "LIVRE" },
11
+ ];
12
+ function inferTarja(row) {
13
+ const text = [row.categoria_regulatoria, row.classe_terapeutica].filter(Boolean).join(" ");
14
+ for (const { pattern, tarja } of TARJA_KEYWORDS) {
15
+ if (pattern.test(text))
16
+ return tarja;
17
+ }
18
+ return undefined;
19
+ }
20
+ function rowToResumo(row) {
21
+ return {
22
+ numProcesso: row.numero_processo ?? row.numero_registro ?? String(row.rowid),
23
+ nomeProduto: row.nome_produto,
24
+ empresa: row.empresa ?? "",
25
+ dataAtualizacao: row.data_finalizacao ?? undefined,
26
+ };
27
+ }
28
+ function rowToDetalhes(row) {
29
+ return {
30
+ numProcesso: row.numero_processo ?? row.numero_registro ?? String(row.rowid),
31
+ nomeProduto: row.nome_produto,
32
+ empresa: row.empresa ?? "",
33
+ dataAtualizacao: row.data_finalizacao ?? undefined,
34
+ tarja: inferTarja(row),
35
+ classesTerapeuticas: row.classe_terapeutica ? [row.classe_terapeutica] : undefined,
36
+ principioAtivo: row.principio_ativo ?? undefined,
37
+ numeroRegistro: row.numero_registro ?? undefined,
38
+ };
39
+ }
40
+ // ── BularioDatasetSource ──────────────────────────────────────────────────────
41
+ export class BularioDatasetSource {
42
+ name = "anvisa_dados_abertos_sqlite";
43
+ db;
44
+ constructor(dbPath) {
45
+ this.db = new Database(dbPath, { readonly: true });
46
+ this.db.pragma("journal_mode = WAL");
47
+ }
48
+ searchByName(params) {
49
+ const { nome, pagina = 1, count = 10 } = params;
50
+ const offset = (pagina - 1) * count;
51
+ const term = `%${nome}%`;
52
+ const rows = this.db
53
+ .prepare(`SELECT rowid, * FROM medicamentos
54
+ WHERE nome_produto LIKE ? COLLATE NOCASE
55
+ LIMIT ? OFFSET ?`)
56
+ .all(term, count, offset);
57
+ return Promise.resolve(rows.map(rowToResumo));
58
+ }
59
+ searchByPrincipalAtivo(params) {
60
+ const { principioAtivo, pagina = 1, count = 10 } = params;
61
+ const offset = (pagina - 1) * count;
62
+ const term = `%${principioAtivo}%`;
63
+ const rows = this.db
64
+ .prepare(`SELECT rowid, * FROM medicamentos
65
+ WHERE principio_ativo LIKE ? COLLATE NOCASE
66
+ LIMIT ? OFFSET ?`)
67
+ .all(term, count, offset);
68
+ return Promise.resolve(rows.map(rowToResumo));
69
+ }
70
+ searchByClasseTerapeutica(params) {
71
+ const { classeTerapeutica, pagina = 1, count = 10 } = params;
72
+ const offset = (pagina - 1) * count;
73
+ const term = `%${classeTerapeutica}%`;
74
+ const rows = this.db
75
+ .prepare(`SELECT rowid, * FROM medicamentos
76
+ WHERE classe_terapeutica LIKE ? COLLATE NOCASE
77
+ LIMIT ? OFFSET ?`)
78
+ .all(term, count, offset);
79
+ return Promise.resolve(rows.map(rowToResumo));
80
+ }
81
+ searchByTarja(params) {
82
+ const { tarja, pagina = 1, count = 10 } = params;
83
+ const offset = (pagina - 1) * count;
84
+ // Heuristic tarja mapping — see inferTarja above
85
+ let whereClause;
86
+ let _bindValue;
87
+ if (tarja === "PRETA") {
88
+ whereClause =
89
+ "(classe_terapeutica LIKE ? OR categoria_regulatoria LIKE ?) " +
90
+ "AND (classe_terapeutica LIKE '%PSICOTR%' OR classe_terapeutica LIKE '%ENTORP%' OR " +
91
+ "classe_terapeutica LIKE '%CONTROLE ESPECIAL%')";
92
+ _bindValue = "%";
93
+ }
94
+ else if (tarja === "VERMELHA") {
95
+ whereClause =
96
+ "(classe_terapeutica LIKE '%ANTIMICROBIANO%' OR classe_terapeutica LIKE '%ANTIBI%' " +
97
+ "OR categoria_regulatoria IN ('Similar', 'Novo', 'Genérico', 'Biológico')) " +
98
+ "AND (classe_terapeutica NOT LIKE '%PSICOTR%')";
99
+ _bindValue = "%";
100
+ }
101
+ else {
102
+ // LIVRE — baixo risco
103
+ whereClause =
104
+ "categoria_regulatoria LIKE '%BAIXO RISCO%' OR categoria_regulatoria LIKE '%ISENTO%'";
105
+ _bindValue = "%";
106
+ }
107
+ let rows;
108
+ if (tarja === "VERMELHA" || tarja === "PRETA") {
109
+ rows = this.db
110
+ .prepare(`SELECT rowid, * FROM medicamentos WHERE ${whereClause} LIMIT ? OFFSET ?`)
111
+ .all(count, offset);
112
+ }
113
+ else {
114
+ rows = this.db
115
+ .prepare(`SELECT rowid, * FROM medicamentos WHERE ${whereClause} LIMIT ? OFFSET ?`)
116
+ .all(count, offset);
117
+ }
118
+ return Promise.resolve(rows.map(rowToResumo));
119
+ }
120
+ getDetalhes(numProcesso) {
121
+ const row = (this.db
122
+ .prepare(`SELECT rowid, * FROM medicamentos
123
+ WHERE numero_processo = ? OR numero_registro = ?
124
+ LIMIT 1`)
125
+ .get(numProcesso, numProcesso) ?? null);
126
+ if (!row) {
127
+ return Promise.reject(new Error(`Medicamento não encontrado para numProcesso: ${numProcesso}`));
128
+ }
129
+ return Promise.resolve(rowToDetalhes(row));
130
+ }
131
+ getApresentacoes(numProcesso) {
132
+ // O CSV de dados abertos não contém apresentações detalhadas.
133
+ // Retornamos a categoria regulatória como informação disponível.
134
+ const row = (this.db
135
+ .prepare(`SELECT rowid, * FROM medicamentos
136
+ WHERE numero_processo = ? OR numero_registro = ?
137
+ LIMIT 1`)
138
+ .get(numProcesso, numProcesso) ?? null);
139
+ if (!row)
140
+ return Promise.resolve([]);
141
+ const apresentacoes = [];
142
+ if (row.categoria_regulatoria) {
143
+ apresentacoes.push({
144
+ descricao: `Categoria: ${row.categoria_regulatoria}`,
145
+ });
146
+ }
147
+ if (row.situacao_registro) {
148
+ apresentacoes.push({
149
+ descricao: `Situação: ${row.situacao_registro}`,
150
+ });
151
+ }
152
+ if (row.principio_ativo) {
153
+ apresentacoes.push({
154
+ descricao: `Princípio Ativo: ${row.principio_ativo}`,
155
+ });
156
+ }
157
+ return Promise.resolve(apresentacoes);
158
+ }
159
+ getBulaLink(_idBulaProtegido) {
160
+ // Não disponível no CSV de dados abertos (v2.0 — texto completo em v2.1 via Worker)
161
+ return Promise.resolve({ id: _idBulaProtegido });
162
+ }
163
+ close() {
164
+ this.db.close();
165
+ return Promise.resolve();
166
+ }
167
+ }
168
+ //# sourceMappingURL=dataset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../src/db/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Schema SQLite para o dataset ANVISA Dados Abertos — Medicamentos
3
+ * Fonte: https://dados.anvisa.gov.br/dados/DADOS_ABERTOS_MEDICAMENTOS.csv
4
+ * Colunas CSV (separador `;`, encoding Latin1/ISO-8859-1):
5
+ * TIPO_PRODUTO; NOME_PRODUTO; DATA_FINALIZACAO_PROCESSO; CATEGORIA_REGULATORIA;
6
+ * NUMERO_REGISTRO_PRODUTO; DATA_VENCIMENTO_REGISTRO; NUMERO_PROCESSO;
7
+ * CLASSE_TERAPEUTICA; EMPRESA_DETENTORA_REGISTRO; SITUACAO_REGISTRO; PRINCIPIO_ATIVO
8
+ */
9
+ export declare const SCHEMA_SQL = "\nPRAGMA journal_mode=WAL;\n\nCREATE TABLE IF NOT EXISTS medicamentos (\n rowid INTEGER PRIMARY KEY AUTOINCREMENT,\n tipo_produto TEXT,\n nome_produto TEXT NOT NULL,\n data_finalizacao TEXT,\n categoria_regulatoria TEXT,\n numero_registro TEXT,\n data_vencimento TEXT,\n numero_processo TEXT,\n classe_terapeutica TEXT,\n empresa TEXT,\n situacao_registro TEXT,\n principio_ativo TEXT\n);\n\nCREATE INDEX IF NOT EXISTS idx_nome ON medicamentos(nome_produto COLLATE NOCASE);\nCREATE INDEX IF NOT EXISTS idx_principio ON medicamentos(principio_ativo COLLATE NOCASE);\nCREATE INDEX IF NOT EXISTS idx_classe ON medicamentos(classe_terapeutica COLLATE NOCASE);\nCREATE INDEX IF NOT EXISTS idx_empresa ON medicamentos(empresa COLLATE NOCASE);\nCREATE INDEX IF NOT EXISTS idx_num_processo ON medicamentos(numero_processo);\nCREATE INDEX IF NOT EXISTS idx_situacao ON medicamentos(situacao_registro);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS medicamentos_fts USING fts5(\n nome_produto,\n principio_ativo,\n classe_terapeutica,\n content='medicamentos',\n content_rowid='rowid'\n);\n\nCREATE TABLE IF NOT EXISTS dataset_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n";
10
+ export declare const SCHEMA_VERSION = "2";
11
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,UAAU,4vCAqCtB,CAAC;AAEF,eAAO,MAAM,cAAc,MAAM,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Schema SQLite para o dataset ANVISA Dados Abertos — Medicamentos
3
+ * Fonte: https://dados.anvisa.gov.br/dados/DADOS_ABERTOS_MEDICAMENTOS.csv
4
+ * Colunas CSV (separador `;`, encoding Latin1/ISO-8859-1):
5
+ * TIPO_PRODUTO; NOME_PRODUTO; DATA_FINALIZACAO_PROCESSO; CATEGORIA_REGULATORIA;
6
+ * NUMERO_REGISTRO_PRODUTO; DATA_VENCIMENTO_REGISTRO; NUMERO_PROCESSO;
7
+ * CLASSE_TERAPEUTICA; EMPRESA_DETENTORA_REGISTRO; SITUACAO_REGISTRO; PRINCIPIO_ATIVO
8
+ */
9
+ export const SCHEMA_SQL = `
10
+ PRAGMA journal_mode=WAL;
11
+
12
+ CREATE TABLE IF NOT EXISTS medicamentos (
13
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
14
+ tipo_produto TEXT,
15
+ nome_produto TEXT NOT NULL,
16
+ data_finalizacao TEXT,
17
+ categoria_regulatoria TEXT,
18
+ numero_registro TEXT,
19
+ data_vencimento TEXT,
20
+ numero_processo TEXT,
21
+ classe_terapeutica TEXT,
22
+ empresa TEXT,
23
+ situacao_registro TEXT,
24
+ principio_ativo TEXT
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_nome ON medicamentos(nome_produto COLLATE NOCASE);
28
+ CREATE INDEX IF NOT EXISTS idx_principio ON medicamentos(principio_ativo COLLATE NOCASE);
29
+ CREATE INDEX IF NOT EXISTS idx_classe ON medicamentos(classe_terapeutica COLLATE NOCASE);
30
+ CREATE INDEX IF NOT EXISTS idx_empresa ON medicamentos(empresa COLLATE NOCASE);
31
+ CREATE INDEX IF NOT EXISTS idx_num_processo ON medicamentos(numero_processo);
32
+ CREATE INDEX IF NOT EXISTS idx_situacao ON medicamentos(situacao_registro);
33
+
34
+ CREATE VIRTUAL TABLE IF NOT EXISTS medicamentos_fts USING fts5(
35
+ nome_produto,
36
+ principio_ativo,
37
+ classe_terapeutica,
38
+ content='medicamentos',
39
+ content_rowid='rowid'
40
+ );
41
+
42
+ CREATE TABLE IF NOT EXISTS dataset_meta (
43
+ key TEXT PRIMARY KEY,
44
+ value TEXT NOT NULL
45
+ );
46
+ `;
47
+ export const SCHEMA_VERSION = "2";
48
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCzB,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC"}
@@ -1,4 +1,3 @@
1
- import type { CacheStore } from "../cache/types.js";
2
1
  import type { Apresentacao, BulaLink, BularioSource, MedicamentoDetalhes, MedicamentoResumo, SearchByClasseTerapeuticaParams, SearchByNameParams, SearchByPrincipalAtivoParams, SearchByTarjaParams } from "../sources/types.js";
3
2
  export type Meta = {
4
3
  data_da_base: string;
@@ -30,11 +29,19 @@ export interface IBularioRepository {
30
29
  getApresentacoes(numProcesso: string): Promise<ResponseWithMeta<Apresentacao[]>>;
31
30
  getBulaLink(idBulaProtegido: string): Promise<ResponseWithMeta<BulaLink>>;
32
31
  }
32
+ /**
33
+ * BularioRepository v2.0
34
+ *
35
+ * Accesses the ANVISA dataset directly via BularioDatasetSource (SQLite).
36
+ * The old multi-source + cache pattern is replaced by a single SQLite source.
37
+ * _meta.modo is always "cache_local" (data is pre-synced locally).
38
+ */
33
39
  export declare class BularioRepository implements IBularioRepository {
34
- private readonly cache;
35
- private readonly sources;
40
+ private readonly source;
36
41
  private readonly degradedThresholdDays;
37
- constructor(cache: CacheStore, sources: BularioSource[], degradedThresholdDays?: number);
42
+ private readonly dataBase;
43
+ constructor(source: BularioSource, degradedThresholdDays?: number);
44
+ private buildResponse;
38
45
  searchByName(params: SearchByNameParams): Promise<ResponseWithMeta<MedicamentoResumo[]>>;
39
46
  searchByPrincipalAtivo(params: SearchByPrincipalAtivoParams): Promise<ResponseWithMeta<MedicamentoResumo[]>>;
40
47
  searchByClasseTerapeutica(params: SearchByClasseTerapeuticaParams): Promise<ResponseWithMeta<MedicamentoResumo[]>>;
@@ -42,6 +49,5 @@ export declare class BularioRepository implements IBularioRepository {
42
49
  getDetalhes(numProcesso: string): Promise<ResponseWithMeta<MedicamentoDetalhes>>;
43
50
  getApresentacoes(numProcesso: string): Promise<ResponseWithMeta<Apresentacao[]>>;
44
51
  getBulaLink(idBulaProtegido: string): Promise<ResponseWithMeta<BulaLink>>;
45
- private _withCache;
46
52
  }
47
53
  //# sourceMappingURL=repository.d.ts.map