{
 "grupo": "web",
 "descricao": "Web e API",
 "itens": [
  {
   "id": "web-curl-get",
   "nome": "Chamar API com GET",
   "receita": "curl -sS -H 'Accept: application/json' url ",
   "prova": " jq ."
  },
  {
   "id": "web-curl-post",
   "nome": "Chamar API com POST",
   "receita": "curl -sS -X POST -H 'Content-Type: application/json' -d '{\"a\":1}' url",
   "prova": "status e corpo lidos"
  },
  {
   "id": "web-auth-bearer",
   "nome": "API com token",
   "receita": "header Authorization: Bearer $TOKEN; token vem de variável, nunca do chat",
   "prova": "200 sem token impresso"
  },
  {
   "id": "web-oauth",
   "nome": "Fluxo OAuth",
   "receita": "redirect, código, troca por token no servidor; state anti-CSRF; segredo só no servidor",
   "prova": "token obtido e usado"
  },
  {
   "id": "web-webhook",
   "nome": "Receber webhook",
   "receita": "responda 200 rápido; valide assinatura; processe em fila; seja idempotente",
   "prova": "evento de teste processado uma vez"
  },
  {
   "id": "web-ratelimit",
   "nome": "Respeitar limite de API",
   "receita": "leia Retry-After; backoff; fila; cache",
   "prova": "sem 429 em 100 chamadas"
  },
  {
   "id": "web-paginacao",
   "nome": "Percorrer API paginada",
   "receita": "siga next/cursor até acabar; limite de segurança",
   "prova": "total coletado = total declarado"
  },
  {
   "id": "web-scraping",
   "nome": "Extrair dados de página",
   "receita": "API oficial primeiro; senão HTML estável (seletor); respeite robots e termos; sem login alheio",
   "prova": "10 itens extraídos conferidos"
  },
  {
   "id": "web-navegador-headless",
   "nome": "Automatizar navegador",
   "receita": "Playwright: goto, wait por seletor (não sleep), screenshot; trace em falha",
   "prova": "execução repetida passa"
  },
  {
   "id": "web-sitemap",
   "nome": "Mapear um site",
   "receita": "sitemap.xml, robots.txt, links internos; liste rotas",
   "prova": "lista de URLs"
  },
  {
   "id": "web-seo-basico",
   "nome": "SEO técnico",
   "receita": "title/description únicos, h1, canonical, sitemap, velocidade, dados estruturados",
   "prova": "Lighthouse SEO verde"
  },
  {
   "id": "web-performance-pagina",
   "nome": "Página mais rápida",
   "receita": "imagens WebP e dimensionadas, fonte com swap, JS dividido, cache longo",
   "prova": "LCP antes e depois"
  },
  {
   "id": "web-headers",
   "nome": "Headers de segurança",
   "receita": "CSP, HSTS, X-Content-Type-Options, Referrer-Policy",
   "prova": "curl -I confere"
  },
  {
   "id": "web-cache-http",
   "nome": "Cache HTTP",
   "receita": "Cache-Control com max-age; ETag; arquivo com hash no nome pode ser immutable",
   "prova": "segunda requisição vem do cache"
  },
  {
   "id": "web-dns",
   "nome": "Apontar domínio",
   "receita": "registro A/CNAME no provedor; dig +short; espere propagar",
   "prova": "dig devolve o IP novo"
  },
  {
   "id": "web-https",
   "nome": "Certificado HTTPS",
   "receita": "Let's Encrypt/Cloudflare; renovação automática; redirecione http para https",
   "prova": "curl -I https 200"
  },
  {
   "id": "web-cors-config",
   "nome": "Liberar CORS",
   "receita": "origem específica (não *, se tiver credenciais); responda OPTIONS",
   "prova": "chamada do navegador passa"
  },
  {
   "id": "web-openapi",
   "nome": "Descrever API",
   "receita": "OpenAPI 3: caminhos, esquemas, erros; gere cliente e validação",
   "prova": "swagger valida"
  },
  {
   "id": "web-websocket",
   "nome": "Tempo real",
   "receita": "conexão, reconexão com backoff, heartbeat, mensagens com id",
   "prova": "derrubar e voltar a conexão funciona"
  },
  {
   "id": "web-feed-rss",
   "nome": "Ler/gerar RSS",
   "receita": "feed.xml com título, link, data, guid estável",
   "prova": "leitor de feed abre"
  }
 ]
}
