- 55 arquivos .md renomeados para sem extensão - Convenção: artefatos de conhecimento sem extensão, scripts (.sh) e dados (.json) mantêm extensão - Scripts atualizados: gerar-manifesto.sh, atualizar-agente.sh, atualizar-agente.global.sh, gerar-agent-json.sh, verificar-linhas.sh - Documentação reescrita: AGENTS, README, EXTEND para refletir uso como submodule .agent direto - manifesto.json regenerado v3.0.0 - Todas as referências internas entre artefatos atualizadas BREAKING CHANGE: arquivos não possuem mais extensão .md
184 lines
4.6 KiB
Text
184 lines
4.6 KiB
Text
---
|
|
name: criar-schema-zod
|
|
scope: global
|
|
description: >
|
|
Guia operacional para criar, organizar e manter schemas de validação Zod em projetos TypeScript.
|
|
---
|
|
|
|
# Skill: Criar Schema Zod
|
|
|
|
Use esta skill ao adicionar validação de dados em qualquer ponto de entrada do sistema — API, formulário, webhook, importação, fila ou variáveis de ambiente.
|
|
|
|
---
|
|
|
|
## PASSO 1 — Identificar a Entrada Externa
|
|
|
|
Determinar qual dado externo precisa de validação:
|
|
|
|
| Tipo de entrada | Onde validar |
|
|
|---|---|
|
|
| Corpo de requisição (POST/PUT) | Handler da rota, antes de qualquer lógica |
|
|
| Query/path params | Handler da rota |
|
|
| Formulário frontend | Antes de chamar a API |
|
|
| Dados de fila (BullMQ) | No processador da fila |
|
|
| Arquivo importado (CSV/JSON) | No parser, linha a linha |
|
|
| Variáveis de ambiente | No boot da aplicação |
|
|
| Resposta de API externa | Após receber a resposta |
|
|
|
|
---
|
|
|
|
## PASSO 2 — Localizar ou Criar o Schema no Pacote Comuns
|
|
|
|
Schemas compartilhados vivem no pacote `*-comuns`:
|
|
|
|
```
|
|
projeto-comuns/src/tabelas/
|
|
├── usuarios/
|
|
│ ├── zUsuarioCriar.ts
|
|
│ ├── zUsuarioEditar.ts
|
|
│ └── index.ts
|
|
└── index.ts
|
|
```
|
|
|
|
### 2.1. Verificar se já existe
|
|
|
|
```bash
|
|
rg "z{NomeDaEntidade}" --include='*.ts' -l
|
|
```
|
|
|
|
Se já existir → reutilizar ou compor a partir dele (`.extend()`, `.pick()`, `.partial()`).
|
|
|
|
### 2.2. Criar o schema
|
|
|
|
```typescript
|
|
import { z } from "zod"
|
|
|
|
export const zUsuarioCriar = z.object({
|
|
nome: z.string().trim().min(1, "Nome é obrigatório"),
|
|
email: z.string().email("E-mail inválido"),
|
|
ativo: z.boolean().default(true),
|
|
})
|
|
```
|
|
|
|
### 2.3. Nomenclatura obrigatória
|
|
|
|
| Padrão | Uso |
|
|
|---|---|
|
|
| `z{Entidade}Criar` | Criação |
|
|
| `z{Entidade}Editar` | Edição (geralmente `.partial()`) |
|
|
| `z{Entidade}Filtro` | Filtros de listagem |
|
|
| `z{Entidade}Resposta` | Resposta de API |
|
|
| `z{Entidade}Params` | Parâmetros de rota |
|
|
| `z{Modulo}Config` | Configuração |
|
|
| `z{Modulo}Ambiente` | Variáveis de ambiente |
|
|
|
|
---
|
|
|
|
## PASSO 3 — Derivar o Tipo com `z.infer<>`
|
|
|
|
```typescript
|
|
export type tipoUsuarioCriar = z.infer<typeof zUsuarioCriar>
|
|
```
|
|
|
|
**Nunca** criar um `interface` ou `type` manual separado do schema. O tipo **sempre** deriva do schema.
|
|
|
|
---
|
|
|
|
## PASSO 4 — Implementar `safeParse` no Ponto de Entrada
|
|
|
|
### Backend (Express)
|
|
|
|
```typescript
|
|
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
|
|
|
|
app.post("/usuarios", async (req, res) => {
|
|
const resultado = zUsuarioCriar.safeParse(req.body)
|
|
if (!resultado.success) {
|
|
return res.status(400).json({ erro: resultado.error.issues[0].message })
|
|
}
|
|
const usuario = await criarUsuario(resultado.data)
|
|
return res.json(usuario)
|
|
})
|
|
```
|
|
|
|
### Frontend (Vue)
|
|
|
|
```typescript
|
|
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
|
|
|
|
const salvar = async () => {
|
|
const resultado = zUsuarioCriar.safeParse(formulario.value)
|
|
if (!resultado.success) {
|
|
erros.value = resultado.error.flatten().fieldErrors
|
|
return
|
|
}
|
|
await api.post("/usuarios", resultado.data)
|
|
}
|
|
```
|
|
|
|
### Variáveis de ambiente
|
|
|
|
```typescript
|
|
const zAmbiente = z.object({
|
|
DATABASE_URL: z.string().url(),
|
|
REDIS_HOST: z.string().default("localhost"),
|
|
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
|
})
|
|
|
|
// Aqui o throw é desejável — a app não deve subir com env inválido
|
|
export const ambiente = () => zAmbiente.parse(process.env)
|
|
```
|
|
|
|
---
|
|
|
|
## PASSO 5 — Compor Schemas Derivados
|
|
|
|
Quando precisar de variações do mesmo schema, **compor** em vez de duplicar:
|
|
|
|
```typescript
|
|
// Schema base
|
|
const zProduto = z.object({
|
|
nome: z.string().trim().min(1),
|
|
preco: z.coerce.number().positive(),
|
|
categoria: z.string(),
|
|
})
|
|
|
|
// Edição: todos os campos opcionais
|
|
const zProdutoEditar = zProduto.partial()
|
|
|
|
// Resposta: base + campos gerados
|
|
const zProdutoResposta = zProduto.extend({
|
|
codigo: z.string().uuid(),
|
|
criadoEm: z.string().datetime(),
|
|
})
|
|
|
|
// Filtro: subset de campos opcionais
|
|
const zProdutoFiltro = zProduto.pick({ categoria: true }).partial()
|
|
```
|
|
|
|
---
|
|
|
|
## PASSO 6 — Validar Tipagem
|
|
|
|
```bash
|
|
npx tsc --noEmit
|
|
```
|
|
|
|
Zero erros = concluído.
|
|
|
|
---
|
|
|
|
## Checklist Final
|
|
|
|
- [ ] O schema está no pacote `*-comuns` (quando compartilhado entre front/back)?
|
|
- [ ] O tipo é derivado do schema com `z.infer<>`?
|
|
- [ ] A validação usa `safeParse` (exceto em boot/middleware)?
|
|
- [ ] A nomenclatura segue o padrão `z{Entidade}{Acao}`?
|
|
- [ ] Transformações (trim, coerce, default) estão no schema, não no handler?
|
|
- [ ] Schemas derivados usam composição (.partial, .extend, .pick), não duplicação?
|
|
- [ ] `tsc --noEmit` passa sem erros?
|
|
|
|
## Referências
|
|
|
|
- Rule: `global/rules/zod-validacao-padrao`
|
|
- Rule: `global/rules/typescript-sem-any`
|