Transparência técnica

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.

Pipeline de extração

Do site oficial ao JSON em 4 etapas

01

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.

cron: '0 6 1 * *' → Ubuntu latest
02

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.

fetch nativo → Playwright (fallback JS-heavy) → HTML bruto
03

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.

Haiku 4.5 (80%) → Sonnet 4.5 (20% URLs críticas)
04

Zod valida, Git versionia

Cada campo do JSON é validado contra o schema Zod antes de qualquer escrita. Falha na validação interrompe o processo — nada inválido é publicado. O snapshot vai para data/current/ e uma cópia arquivada para data/history/. Se houver mudança significativa, uma issue é aberta automaticamente no GitHub.

Zod schema → data/current/ → data/history/ → GitHub Issue
.github/workflows/monthly-update.yml
 1on:
 2  schedule:
 3    # Dia 1 de cada mês, 06:00 UTC
 4    - cron: '0 6 1 * *'
 5  workflow_dispatch: # disparo manual
 6
 7jobs:
 8  update:
 9    runs-on: ubuntu-latest
10    steps:
11      - name: Checkout
12        uses: actions/checkout@v4
13
14      - name: Setup Bun
15        uses: oven-sh/setup-bun@v2
16
17      - name: Instalar dependências
18        run: bun install
19
20      - name: Extrair todos os países
21        run: bun run extract
22        env:
23          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
24
25      - name: Detectar mudanças
26        run: bun run diff
27
28      - name: Publicar no GitHub Pages
29        uses: peaceiris/actions-gh-pages@v4
30        with:
31          publish_dir: ./data/current
32          publish_branch: gh-pages
Contrato de dados

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.

data/current/nl.json (resumido)
 1{
 2  "country": "nl",
 3  "lastUpdated": "2026-04-01T06:00:00Z",
 4  "visaTypes": [{
 5    "id": "highly-skilled-migrant",
 6    "name": "Altamente Qualificado",
 7    "processingTime": "2-4 semanas",
 8    "requirements": {
 9      "incomeRequirement": {
10        "amount": 5688,
11        "currency": "EUR",
12        "period": "monthly"
13      }
14    }
15  }],
16  "reliability": {
17    "extractionConfidence": "high",
18    "lastVerified": "2026-04-01"
19  }
20}
src/extractors/schema.ts (campos principais)
 1const VisaTypeSchema = z.object({
 2  id:            z.string(),
 3  name:          z.string(),
 4  processingTime: z.string().optional(),
 5  requirements:  z.object({
 6    incomeRequirement: z.object({
 7      amount:   z.number(),
 8      currency: z.literal('EUR'),
 9      period:   z.enum(['monthly','annual'])
10    }).optional()
11  })
12})
13
14const CountrySchema = z.object({
15  country:     z.string().length(2),
16  lastUpdated: z.string().datetime(),
17  visaTypes:   z.array(VisaTypeSchema),
18  reliability: ReliabilitySchema
19})
Stack técnico

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.

Runtime
Bun 1.1+

Fetch nativo sem polyfill, runtime de testes embutido, instalação única. Compatível com Node 20+ se necessário.

Linguagem
TypeScript estrito

Schema tipado impede campos ausentes silenciosos. O tsc --noEmit roda no CI antes de qualquer extração.

LLM
Anthropic API — estratégia 80/20

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.

Validação
Zod

Schema declarativo colocado em src/extractors/schema.ts. Falha de parse lança exceção e impede publicação de dados inválidos.

Storage
JSON versionado no Git

Zero infraestrutura. O histórico mensal é o próprio data/history/. Diff visual no GitHub. Qualquer um pode consumir via CDN sem autenticação.

CI
GitHub Actions

Free tier cobre folgadamente um cron mensal. O log de cada extração fica arquivado por 90 dias na aba Actions.

Dados abertos

Como consumir o JSON

Os snapshots ficam disponíveis publicamente via GitHub Pages, com CORS aberto. Nenhuma chave de API necessária.

Endpoints disponíveis
# 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.