Como os dados chegam
até você.
Cada número neste site foi extraído de uma fonte oficial de imigração por um pipeline automático. Esta página explica exatamente como.
Do site oficial ao JSON em 4 etapas
GitHub Actions dispara o cron
No dia 1 de cada mês às 06:00 UTC, o workflow monthly-update.yml inicia.
Nenhuma intervenção manual é necessária. O histórico de execuções fica visível
na aba Actions do repositório.
Fetcher visita as URLs oficiais
Para cada país, o fetcher acessa as URLs declaradas em src/sources/{cc}.ts
usando fetch nativo do Bun. Sites que exigem JavaScript recebem
fallback via Playwright headless. Mínimo de 2 segundos entre requests no mesmo domínio.
LLM extrai dados estruturados
O HTML é processado pelo @mozilla/readability para isolar o conteúdo
principal. O texto limpo é enviado para a API da Anthropic. Claude Haiku 4.5 processa
a maioria das URLs. URLs marcadas como críticas usam Claude Sonnet 4.5.
O output é um JSON seguindo o schema declarado.
Zod valida, travas verificam, Git versionia
Cada campo do JSON é validado contra o schema Zod antes de qualquer escrita.
Depois, a reconciliação de IDs mapeia cada visto extraído para seu ID canônico do
ciclo anterior (evita que nomes ligeiramente diferentes criem duplicatas).
O guard detecta degradação estrutural e aborta o commit se o resultado
piorou. O audit compara valores críticos contra os
verificationUrls oficiais e anota divergências maiores que 5%.
Só então o snapshot vai para data/current/ e o site é gerado.
1on: 2 schedule: 3 # Dia 1 de cada mês, 06:00 UTC 4 - cron: '0 6 1 * *' 5 workflow_dispatch: # disparo manual 6 7steps: 8 - bun run validate # valida JSON atual antes 9 - bun run extract # extrai todos os países 10 - bun run patch:all # reaplica enriquecimentos manuais 11 - bun run validate # valida resultado extraído 12 - bun run guard # trava: aborta se extração degradou 13 - bun run audit # auditoria: compara vs verificationUrls 14 - bun run index # gera index.json 15 - bun run site:generate # gera as páginas HTML 16 - bun run diff # detecta mudanças significativas 17 18 # Commit e deploy ocorrem somente se guard passou 19 - uses: peaceiris/actions-gh-pages@v4 20 with: 21 publish_dir: ./data/current 22 publish_branch: gh-pages
O que cada JSON contém
Todos os 10 países seguem o mesmo schema. Os campos são validados pelo Zod antes de qualquer publicação. Se um campo obrigatório vier vazio ou com tipo errado, o snapshot inteiro é rejeitado.
1{ 2 "meta": { 3 "country": "nl", 4 "countryName": "Países Baixos", 5 "lastUpdated": "2026-06-01T06:00:00Z" 6 }, 7 "visaTypes": [{ 8 "id": "highly-skilled-migrant", 9 "name": "Altamente Qualificado", 10 "requirements": { 11 "incomeRequirement": { 12 "amount": 5688, 13 "currency": "EUR", 14 "period": "monthly" 15 } 16 } 17 }], 18 "reliability": { 19 "extractedBy": "llm", 20 "extractionConfidence": "high", 21 "humanReviewedAt": null, 22 "knownIssues": [] 23 } 24}
1const MoneyAmountSchema = z.object({ 2 amount: z.number().nonnegative(), 3 currency: z.enum(['EUR','USD','BRL','AUD']), 4 period: z.enum(['one-time','monthly','yearly','total']) 5 .nullable().optional(), 6}) 7 8const ReliabilitySchema = z.object({ 9 extractedBy: z.enum(['llm','manual']), 10 extractionConfidence: z.enum(['high','medium','low']), 11 humanReviewedAt: z.string().datetime().nullable(), 12 knownIssues: z.array(z.string()), 13}) 14 15const CountryDataSchema = z.object({ 16 meta: MetaSchema, 17 visaTypes: z.array(VisaTypeSchema), 18 generalRequirements: GeneralRequirementsSchema, 19 reliability: ReliabilitySchema, 20})
Tecnologias e por que cada uma
Cada decisão foi tomada para minimizar infraestrutura, maximizar confiabilidade e manter o custo mensal abaixo de USD 1.
Fetch nativo sem polyfill, runtime de testes embutido, instalação única. Compatível com Node 20+ se necessário.
Schema tipado impede campos ausentes silenciosos. O tsc --noEmit
roda no CI antes de qualquer extração.
Haiku 4.5 para a maioria das URLs (barato, rápido). Sonnet 4.5 apenas para URLs marcadas como críticas. Custo estimado: USD 4 a 10 por ano.
Schema declarativo colocado em src/extractors/schema.ts.
Falha de parse lança exceção e impede publicação de dados inválidos.
Zero infraestrutura. O histórico mensal é o próprio data/history/.
Diff visual no GitHub. Qualquer um pode consumir via CDN sem autenticação.
Free tier cobre folgadamente um cron mensal. O log de cada extração fica arquivado por 90 dias na aba Actions.
Como consumir o JSON
Os snapshots ficam disponíveis publicamente via GitHub Pages, com CORS aberto. Nenhuma chave de API necessária.
# País específico https://vl-builds.github.io/rota-legal-monitor/nl.json https://vl-builds.github.io/rota-legal-monitor/pt.json https://vl-builds.github.io/rota-legal-monitor/de.json # Índice de todos os países https://vl-builds.github.io/rota-legal-monitor/index.json # Países disponíveis: nl pt de es ie it fr be at au
O código é aberto
Inspecione o pipeline, proponha melhorias ou adicione um novo país. Todo o processo está no repositório público.