@evertonferreira96/agents 0.0.0-stage → 0.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.
- package/.claude/agents/api.md +45 -0
- package/.claude/agents/app.md +41 -0
- package/.claude/agents/architect.md +49 -0
- package/.claude/agents/infra.md +27 -0
- package/.claude/agents/product.md +58 -0
- package/.claude/agents/quick.md +33 -0
- package/.claude/agents/security.md +39 -0
- package/.claude/agents/ui.md +54 -0
- package/.claude/agents/verifier.md +56 -0
- package/.claude/agents/web.md +41 -0
- package/.claude/hooks/config-update-check.mjs +14 -0
- package/.claude/hooks/context-budget.mjs +51 -0
- package/.claude/hooks/graphify-update.sh +16 -0
- package/.claude/hooks/guard-bash.sh +64 -0
- package/.claude/hooks/orchestrator-guard.sh +49 -0
- package/.claude/hooks/post-compact.sh +26 -0
- package/.claude/hooks/readonly-guard.sh +54 -0
- package/.claude/hooks/stop-verify.sh +52 -0
- package/.claude/rules/api.md +21 -0
- package/.claude/rules/db.md +9 -0
- package/.claude/rules/delivery-diagram.md +16 -0
- package/.claude/rules/git-workflow.md +34 -0
- package/.claude/rules/graphify.md +3 -0
- package/.claude/rules/mobile.md +22 -0
- package/.claude/rules/tickets.md +9 -0
- package/.claude/rules/ui.md +14 -0
- package/.claude/rules/web.md +27 -0
- package/.claude/settings.json +114 -0
- package/.claude/skills/animate/RECIPES.md +324 -0
- package/.claude/skills/animate/SKILL.md +208 -0
- package/.claude/skills/animate/agents/openai.yaml +5 -0
- package/.claude/skills/animate-expo/RECIPES.md +385 -0
- package/.claude/skills/animate-expo/SKILL.md +264 -0
- package/.claude/skills/animate-expo/agents/openai.yaml +5 -0
- package/.claude/skills/animation-performance/SKILL.md +223 -0
- package/.claude/skills/animation-performance/agents/openai.yaml +5 -0
- package/.claude/skills/apple-design/SKILL.md +291 -0
- package/.claude/skills/apple-design/agents/openai.yaml +5 -0
- package/.claude/skills/arch-api/SKILL.md +291 -0
- package/.claude/skills/arch-api/agents/openai.yaml +3 -0
- package/.claude/skills/arch-api/references/common.md +174 -0
- package/.claude/skills/arch-api/references/contract.md +289 -0
- package/.claude/skills/arch-api/references/domain.md +301 -0
- package/.claude/skills/arch-api/references/endpoints.md +184 -0
- package/.claude/skills/arch-api/references/review.md +228 -0
- package/.claude/skills/arch-api/references/security.md +151 -0
- package/.claude/skills/arch-app/SKILL.md +278 -0
- package/.claude/skills/arch-app/agents/openai.yaml +3 -0
- package/.claude/skills/arch-app/references/navigation-session.md +257 -0
- package/.claude/skills/arch-app/references/review.md +155 -0
- package/.claude/skills/arch-app/references/screens.md +214 -0
- package/.claude/skills/arch-app/references/services.md +301 -0
- package/.claude/skills/arch-app/references/ui-mobile.md +124 -0
- package/.claude/skills/arch-db/SKILL.md +181 -0
- package/.claude/skills/arch-db/agents/openai.yaml +3 -0
- package/.claude/skills/arch-db/references/indexes-queries.md +251 -0
- package/.claude/skills/arch-db/references/migrations.md +191 -0
- package/.claude/skills/arch-db/references/modeling.md +214 -0
- package/.claude/skills/arch-db/references/mysql.md +262 -0
- package/.claude/skills/arch-db/references/pooling-ops.md +112 -0
- package/.claude/skills/arch-db/references/review.md +143 -0
- package/.claude/skills/arch-db/references/transactions-locks.md +141 -0
- package/.claude/skills/arch-web/SKILL.md +307 -0
- package/.claude/skills/arch-web/agents/openai.yaml +3 -0
- package/.claude/skills/arch-web/references/auth-session-env.md +165 -0
- package/.claude/skills/arch-web/references/blocks.md +198 -0
- package/.claude/skills/arch-web/references/cache.md +124 -0
- package/.claude/skills/arch-web/references/review.md +142 -0
- package/.claude/skills/arch-web/references/services.md +297 -0
- package/.claude/skills/arch-web/references/tests.md +159 -0
- package/.claude/skills/arch-web/references/ui-forms.md +143 -0
- package/.claude/skills/break-ui/CATALOG.md +135 -0
- package/.claude/skills/break-ui/SKILL.md +176 -0
- package/.claude/skills/break-ui/agents/openai.yaml +5 -0
- package/.claude/skills/brief/SKILL.md +91 -0
- package/.claude/skills/brief/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/SKILL.md +138 -0
- package/.claude/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/.claude/skills/emil-design-eng/SKILL.md +675 -0
- package/.claude/skills/emil-design-eng/agents/openai.yaml +5 -0
- package/.claude/skills/find-animation-opportunities/SKILL.md +141 -0
- package/.claude/skills/find-animation-opportunities/agents/openai.yaml +5 -0
- package/.claude/skills/graphify/.graphify_version +1 -0
- package/.claude/skills/graphify/SKILL.md +742 -0
- package/.claude/skills/graphify/agents/openai.yaml +3 -0
- package/.claude/skills/graphify/references/add-watch.md +56 -0
- package/.claude/skills/graphify/references/exports.md +91 -0
- package/.claude/skills/graphify/references/extraction-spec.md +70 -0
- package/.claude/skills/graphify/references/github-and-merge.md +46 -0
- package/.claude/skills/graphify/references/hooks.md +33 -0
- package/.claude/skills/graphify/references/query.md +317 -0
- package/.claude/skills/graphify/references/transcribe.md +52 -0
- package/.claude/skills/graphify/references/update.md +221 -0
- package/.claude/skills/grill-me/SKILL.md +7 -0
- package/.claude/skills/grill-me/agents/openai.yaml +5 -0
- package/.claude/skills/grill-with-docs/SKILL.md +64 -0
- package/.claude/skills/grill-with-docs/agents/openai.yaml +5 -0
- package/.claude/skills/grilling/SKILL.md +30 -0
- package/.claude/skills/grilling/agents/openai.yaml +3 -0
- package/.claude/skills/handoff/SKILL.md +28 -0
- package/.claude/skills/handoff/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/SKILL.md +89 -0
- package/.claude/skills/impeccable/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/reference/adapt.md +318 -0
- package/.claude/skills/impeccable/reference/adapt.native.md +58 -0
- package/.claude/skills/impeccable/reference/android.md +46 -0
- package/.claude/skills/impeccable/reference/animate.md +89 -0
- package/.claude/skills/impeccable/reference/audit.md +137 -0
- package/.claude/skills/impeccable/reference/audit.native.md +139 -0
- package/.claude/skills/impeccable/reference/bolder.md +33 -0
- package/.claude/skills/impeccable/reference/clarify.md +94 -0
- package/.claude/skills/impeccable/reference/colorize.md +86 -0
- package/.claude/skills/impeccable/reference/component-review.md +63 -0
- package/.claude/skills/impeccable/reference/craft-floor.md +44 -0
- package/.claude/skills/impeccable/reference/craft.md +5 -0
- package/.claude/skills/impeccable/reference/critique.md +809 -0
- package/.claude/skills/impeccable/reference/degraded/asset-producer.md +42 -0
- package/.claude/skills/impeccable/reference/degraded/documenter.md +24 -0
- package/.claude/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/.claude/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/.claude/skills/impeccable/reference/delight.md +70 -0
- package/.claude/skills/impeccable/reference/distill.md +111 -0
- package/.claude/skills/impeccable/reference/doctor.md +54 -0
- package/.claude/skills/impeccable/reference/document.md +416 -0
- package/.claude/skills/impeccable/reference/extract.md +69 -0
- package/.claude/skills/impeccable/reference/generate.md +101 -0
- package/.claude/skills/impeccable/reference/harden.md +345 -0
- package/.claude/skills/impeccable/reference/hooks.md +113 -0
- package/.claude/skills/impeccable/reference/init.md +131 -0
- package/.claude/skills/impeccable/reference/ios.md +51 -0
- package/.claude/skills/impeccable/reference/layout.md +84 -0
- package/.claude/skills/impeccable/reference/live-setup.md +104 -0
- package/.claude/skills/impeccable/reference/live.md +325 -0
- package/.claude/skills/impeccable/reference/mode-operate.md +21 -0
- package/.claude/skills/impeccable/reference/mode-persuade.md +19 -0
- package/.claude/skills/impeccable/reference/mode-read.md +21 -0
- package/.claude/skills/impeccable/reference/new-work.md +154 -0
- package/.claude/skills/impeccable/reference/onboard.md +234 -0
- package/.claude/skills/impeccable/reference/operate.md +61 -0
- package/.claude/skills/impeccable/reference/optimize.md +258 -0
- package/.claude/skills/impeccable/reference/overdrive.md +127 -0
- package/.claude/skills/impeccable/reference/polish.md +105 -0
- package/.claude/skills/impeccable/reference/quieter.md +99 -0
- package/.claude/skills/impeccable/reference/region-map.md +26 -0
- package/.claude/skills/impeccable/reference/routing.md +24 -0
- package/.claude/skills/impeccable/reference/shape.md +59 -0
- package/.claude/skills/impeccable/reference/typeset.md +80 -0
- package/.claude/skills/impeccable/reference/visualize.md +47 -0
- package/.claude/skills/impeccable/scripts/VERSION +1 -0
- package/.claude/skills/impeccable/scripts/command-metadata.json +98 -0
- package/.claude/skills/impeccable/scripts/data/font-index-failures.json +121 -0
- package/.claude/skills/impeccable/scripts/data/font-index.json +1 -0
- package/.claude/skills/impeccable/scripts/impeccable +206 -0
- package/.claude/skills/impeccable/scripts/impeccable.cmd +214 -0
- package/.claude/skills/impeccable/scripts/live-browser-dom.js +281 -0
- package/.claude/skills/impeccable/scripts/live-browser-ignores.js +242 -0
- package/.claude/skills/impeccable/scripts/live-browser-session.js +148 -0
- package/.claude/skills/impeccable/scripts/live-browser.js +13532 -0
- package/.claude/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/.claude/skills/improve-animations/AUDIT.md +115 -0
- package/.claude/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
- package/.claude/skills/improve-animations/SKILL.md +110 -0
- package/.claude/skills/improve-animations/agents/openai.yaml +5 -0
- package/.claude/skills/mobile-native/SKILL.md +311 -0
- package/.claude/skills/mobile-native/agents/openai.yaml +3 -0
- package/.claude/skills/orchestrate/SKILL.md +181 -0
- package/.claude/skills/orchestrate/agents/openai.yaml +3 -0
- package/.claude/skills/pick-ui-library/SKILL.md +85 -0
- package/.claude/skills/pick-ui-library/agents/openai.yaml +5 -0
- package/.claude/skills/ponytail/SKILL.md +52 -0
- package/.claude/skills/ponytail/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-audit/SKILL.md +103 -0
- package/.claude/skills/ponytail-audit/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-debt/SKILL.md +44 -0
- package/.claude/skills/ponytail-debt/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-review/SKILL.md +101 -0
- package/.claude/skills/ponytail-review/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/SKILL.md +42 -0
- package/.claude/skills/postgres-best-practices/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/references/backup-restore.md +372 -0
- package/.claude/skills/postgres-best-practices/references/bulk-loading.md +315 -0
- package/.claude/skills/postgres-best-practices/references/connection-pooling.md +310 -0
- package/.claude/skills/postgres-best-practices/references/hot-standby.md +221 -0
- package/.claude/skills/postgres-best-practices/references/indexing.md +318 -0
- package/.claude/skills/postgres-best-practices/references/logical-replication.md +610 -0
- package/.claude/skills/postgres-best-practices/references/major-version-upgrades.md +334 -0
- package/.claude/skills/postgres-best-practices/references/performance-diagnostics.md +553 -0
- package/.claude/skills/postgres-best-practices/references/query-optimization.md +294 -0
- package/.claude/skills/postgres-best-practices/references/query-patterns.md +566 -0
- package/.claude/skills/postgres-best-practices/references/schema-design.md +362 -0
- package/.claude/skills/postgres-best-practices/references/security-roles.md +489 -0
- package/.claude/skills/postgres-best-practices/references/transaction-isolation.md +344 -0
- package/.claude/skills/project-docs/SKILL.md +330 -0
- package/.claude/skills/project-docs/agents/openai.yaml +3 -0
- package/.claude/skills/prototype/PICKER.md +197 -0
- package/.claude/skills/prototype/SKILL.md +98 -0
- package/.claude/skills/prototype/agents/openai.yaml +5 -0
- package/.claude/skills/retro/SKILL.md +43 -0
- package/.claude/skills/retro/agents/openai.yaml +5 -0
- package/.claude/skills/review/SKILL.md +74 -0
- package/.claude/skills/review/agents/openai.yaml +3 -0
- package/.claude/skills/review/references/smells.md +23 -0
- package/.claude/skills/review-animations/SKILL.md +120 -0
- package/.claude/skills/review-animations/STANDARDS.md +187 -0
- package/.claude/skills/review-animations/agents/openai.yaml +5 -0
- package/.claude/skills/tdd/SKILL.md +38 -0
- package/.claude/skills/tdd/agents/openai.yaml +3 -0
- package/.claude/skills/tdd/mocking.md +59 -0
- package/.claude/skills/tdd/tests.md +77 -0
- package/.claude/skills/triage/SKILL.md +58 -0
- package/.claude/skills/triage/agents/openai.yaml +3 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +378 -0
- package/.claude/skills/ui-ux-pro-max/agents/openai.yaml +5 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/.claude/skills/writing-for-agents/SKILL.md +81 -0
- package/.claude/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/.worktreeinclude +3 -0
- package/AGENTS.md +44 -0
- package/README.md +14 -2
- package/VERSION +1 -0
- package/bin/agents.mjs +45 -0
- package/docs/adr/README.md +31 -0
- package/docs/brief-template.md +3 -0
- package/docs/plans/README.md +73 -0
- package/package.json +46 -4
- package/scripts/bootstrap.mjs +188 -0
- package/scripts/check-catalog.mjs +43 -0
- package/scripts/skills-update.mjs +51 -0
- package/skills-lock.json +269 -0
- package/templates/CLAUDE.project.md +41 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Templates por tipo de endpoint
|
|
2
|
+
|
|
3
|
+
Abra ao decidir o formato de um endpoint novo (verbo, path, status, erros, autorização, persistência) ou ao conferir um endpoint antes do PR. Nomes são exemplos; o vocabulário do domínio vem do repo. Cada template assume o padrão de props/exec do `SKILL.md` e de `contract.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Templates
|
|
8
|
+
|
|
9
|
+
### 1.1 GET detalhe
|
|
10
|
+
|
|
11
|
+
```txt
|
|
12
|
+
Path /customers/:customerId/invoices/:invoiceRef (ref = id ou código legível, se o domínio tiver)
|
|
13
|
+
operationId getInvoiceByRef status 200 (ok) erros ...docs.response
|
|
14
|
+
Response { data: InvoiceDetail } detalhe nomeado em entities
|
|
15
|
+
Service resolve o pai (404) -> assertCanRead* -> port.find*Detail -> assert*Visible (404) -> mapper
|
|
16
|
+
Adapter leitura por id/código + tenant, relacionados agregados na mesma query
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
404 x 403 para o recurso que o leitor não pode ver: regra 9 do `SKILL.md`, detalhe em `domain.md` §3.
|
|
20
|
+
|
|
21
|
+
### 1.2 GET lista
|
|
22
|
+
|
|
23
|
+
```txt
|
|
24
|
+
Path /customers/:customerId/invoices
|
|
25
|
+
operationId listCustomerInvoices status 200 erros ...docs.response
|
|
26
|
+
Query paginationQuery.extend({ ...filtros, sort: <Domain>ListSort, direction }).strict()
|
|
27
|
+
Response paginatedResponse(invoiceSummarySchema) { data, meta }
|
|
28
|
+
Service resolve o pai -> assertCanRead* -> viewerOf -> port.list* -> mapper por item
|
|
29
|
+
Adapter build*Queries: página + contagem com o mesmo WHERE; sort por mapa; tiebreaker por id; toPaginationMeta
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Offset para telas administrativas pequenas e médias. Feed, log e linha do tempo: cursor keyset opaco (`nextCursor`), `limit + 1` decide se há próxima página.
|
|
33
|
+
|
|
34
|
+
### 1.3 POST criar
|
|
35
|
+
|
|
36
|
+
```txt
|
|
37
|
+
Path /customers/:customerId/invoices
|
|
38
|
+
operationId createInvoice status 201 (created) erros ...docs.response + ...docs.unprocessable (+ ...docs.conflict)
|
|
39
|
+
Body CreateInvoiceBodyRequest (.strict()); opcionais com default de negócio decidido no service
|
|
40
|
+
Response { data: InvoiceDetail } o detalhe recém-criado, mesmo schema do GET
|
|
41
|
+
Service resolve -> assertCanActOn*(…, 'create') -> estado (422) -> referências (422 <domain>.reference_invalid) -> rules -> New* -> port.create* -> detalhe
|
|
42
|
+
Adapter uma transação: sequência/código, linha, filhos, audits; constraint -> 409/422 com code
|
|
43
|
+
Idempotência obrigatória quando duplicar causa dano (Idempotency-Key, domain.md §9)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 1.4 PATCH atualizar parte
|
|
47
|
+
|
|
48
|
+
```txt
|
|
49
|
+
Path /customers/:customerId/invoices/:invoiceId
|
|
50
|
+
operationId updateInvoice status 200 erros ...docs.response + ...docs.conflict (+ ...docs.unprocessable)
|
|
51
|
+
Body UpdateInvoiceBodyRequest: campos opcionais + version (lida pelo client)
|
|
52
|
+
Service resolve -> access -> monta *Write.set só com os campos enviados (undefined = não mexe; null = limpa) -> port.write*
|
|
53
|
+
Adapter updateMany com version no where; 0 linhas = 409 <domain>.stale; audit com previous/current
|
|
54
|
+
Response { data: InvoiceDetail } ou { data, meta: { version } }, conforme o vizinho
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Não reutilize o schema de criação quando os obrigatórios diferem.
|
|
58
|
+
|
|
59
|
+
### 1.5 PUT substituir
|
|
60
|
+
|
|
61
|
+
```txt
|
|
62
|
+
operationId replace<Coisa> ou set<Coisa><Relação> (ex.: setInvoiceTags) status 200
|
|
63
|
+
Body a coleção ou o recurso inteiro; o que não vier é removido
|
|
64
|
+
Adapter substituição na mesma transação (remove o que saiu, insere o que entrou)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Se a API não quer semântica de substituição, use PATCH.
|
|
68
|
+
|
|
69
|
+
### 1.6 DELETE
|
|
70
|
+
|
|
71
|
+
```txt
|
|
72
|
+
operationId delete<Coisa> status 204 (noContent) sem content no documento
|
|
73
|
+
Service access + estado (recurso em uso = 409/422 com code)
|
|
74
|
+
Adapter soft delete quando há auditoria, histórico ou relacionamento crítico (arch-db)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 1.7 POST ação ou comando
|
|
78
|
+
|
|
79
|
+
```txt
|
|
80
|
+
Path /customers/:customerId/invoices/:invoiceId/<acao> ex.: cancel, approve, assign, move
|
|
81
|
+
operationId <acao><Coisa> status 200, 202 ou 204 conforme a semântica
|
|
82
|
+
Service access da ação específica + transição permitida (rules) + version
|
|
83
|
+
Adapter write + audit da transição na mesma transação
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Transição de estado com regra não é PATCH genérico. Avalie idempotência sempre.
|
|
87
|
+
|
|
88
|
+
### 1.8 Upload
|
|
89
|
+
|
|
90
|
+
```txt
|
|
91
|
+
Fluxo pela capacidade de storage do repo (common/storage), em rota ou fluxo próprio
|
|
92
|
+
Limite tamanho máximo explícito da rota; nunca subir o limite global
|
|
93
|
+
Validação mime type, extensão, tamanho, quantidade
|
|
94
|
+
Response { data: <Arquivo>Response }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 1.9 Download
|
|
98
|
+
|
|
99
|
+
```txt
|
|
100
|
+
Response content-type real (application/pdf, text/csv, application/octet-stream ...)
|
|
101
|
+
Headers Content-Type, Content-Disposition, Cache-Control quando aplicável
|
|
102
|
+
Auth access antes de gerar URL ou stream
|
|
103
|
+
Grande streaming ou URL assinada pela capacidade de storage
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 1.10 Público
|
|
107
|
+
|
|
108
|
+
```txt
|
|
109
|
+
Documento security: [] no registerPath (regra 8 do SKILL.md, contract.md §1)
|
|
110
|
+
Runtime sem AuthUserGuard (ou OptionalAuthUserGuard se personaliza)
|
|
111
|
+
Rate limit guard dedicado obrigatório em login, cadastro, recuperação e busca
|
|
112
|
+
Dados nada sensível; cache avaliado em GET público
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 1.11 Protegido com tenant
|
|
116
|
+
|
|
117
|
+
```txt
|
|
118
|
+
Documento security bearer; o header de contexto documentado se o client o envia
|
|
119
|
+
Runtime AuthUserGuard + @<Tenant>Ref() (nunca lança)
|
|
120
|
+
Service resolver do tenant (recusa inexistente, removido, cancelado) -> access com ator + tenant + recurso
|
|
121
|
+
Adapter toda query filtrada pelo tenant
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 1.12 Assíncrono
|
|
125
|
+
|
|
126
|
+
```txt
|
|
127
|
+
Status 202 (accepted) Response { data: { jobId, status } }
|
|
128
|
+
Fluxo service grava o pedido de trabalho (e outbox, se o evento não pode se perder) -> fila -> <assunto>.worker.ts
|
|
129
|
+
Idempotência geralmente obrigatória Log jobId + traceId
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 2. Checklist de endpoint novo
|
|
135
|
+
|
|
136
|
+
Cada item aponta a regra (`SKILL.md`, "Regras não negociáveis") ou a seção que o explica.
|
|
137
|
+
|
|
138
|
+
```txt
|
|
139
|
+
Contrato
|
|
140
|
+
- [ ] props e exec planos em surfaces/, nomes <acao>.<domain>.*.surface.ts SKILL "Nomes"
|
|
141
|
+
- [ ] registerOpenApi + .openapi(nome) + openapi.register em todo schema da operação contract.md §1
|
|
142
|
+
- [ ] operationId camelCase único; tag existente no build contract.md §1
|
|
143
|
+
- [ ] description lista cada code de 409/422 e a permissão regra 7
|
|
144
|
+
- [ ] blocos de erro só os que ocorrem regra 7
|
|
145
|
+
- [ ] .strict() em params, query e body; datas ISO string regras 4, 6
|
|
146
|
+
- [ ] security = guard regra 8
|
|
147
|
+
- [ ] extensões x-* do repo preenchidas, se houver contract.md §1
|
|
148
|
+
|
|
149
|
+
Exec
|
|
150
|
+
- [ ] @Controller() + path do decorator = Endpoint sem a barra regra 1
|
|
151
|
+
- [ ] @HttpCode = status de sucesso do props; Zd* com instructions contract.md §3
|
|
152
|
+
- [ ] só guards, pipes e um execute regra 2
|
|
153
|
+
|
|
154
|
+
Service e domínio
|
|
155
|
+
- [ ] um <Acao>Service por arquivo; type Input escrito à mão regra 3
|
|
156
|
+
- [ ] access antes de qualquer escrita; 404 x 403 decidido regra 9
|
|
157
|
+
- [ ] regra pura em *.rules.ts com spec domain.md §2
|
|
158
|
+
- [ ] resposta pelo mapper, validada contra o schema no spec review.md §1
|
|
159
|
+
|
|
160
|
+
Persistência
|
|
161
|
+
- [ ] port com tipos escritos à mão regra 10
|
|
162
|
+
- [ ] adapter: transação dentro, constraints no mapa de -db-errors regra 14
|
|
163
|
+
- [ ] SQL parametrizado em <domain>.sql.ts; sort por mapa + tiebreaker regra 12
|
|
164
|
+
- [ ] migration e índice revisados com arch-db regra 16
|
|
165
|
+
|
|
166
|
+
Fiação e entrega
|
|
167
|
+
- [ ] exec em controllers, service em providers, import do props no surface.module domain.md §8
|
|
168
|
+
- [ ] port novo ligado no <domain>.sgbds.module e exportado domain.md §8
|
|
169
|
+
- [ ] doc.md atualizado (endpoint x rota x permissão, regras, escrita) regra 17
|
|
170
|
+
- [ ] openapi:generate, check-types, lint e testes verdes SKILL "Comandos"
|
|
171
|
+
- [ ] mudança de contrato classificada contract.md §8
|
|
172
|
+
- [ ] rate limit, idempotência e timeout de provider avaliados quando críticos security.md §3, §9
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 3. Prontidão de produção
|
|
178
|
+
|
|
179
|
+
```txt
|
|
180
|
+
Contrato documento gerado e versionado; client regenerado compila; breaking change avaliado
|
|
181
|
+
Segurança 401 sem token e 403/404 sem acesso testados; cross-tenant testado; nada sensível no log ou no response
|
|
182
|
+
Operação logs estruturados com traceId; timeout e retry em provider externo; idempotência onde duplica
|
|
183
|
+
Dados transação nos multi-writes; constraint para cada invariante; listas com teto; audit nos writes sensíveis
|
|
184
|
+
```
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Testes, revisão e entrega
|
|
2
|
+
|
|
3
|
+
Abra ao escrever testes, ao abrir um domínio novo, ao planejar a migração de um projeto existente para este padrão, ao registrar uma decisão transversal (ADR) ou ao revisar um PR de API. O que é obrigatório em todo endpoint está no `SKILL.md` ("Criando um endpoint" e "Critério de aceite").
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Testes
|
|
8
|
+
|
|
9
|
+
### Onde fica cada um
|
|
10
|
+
|
|
11
|
+
```txt
|
|
12
|
+
services/<acao>.service.spec.ts unit do caso de uso, com port e policy falsos (o grosso da suíte)
|
|
13
|
+
services/<assunto>.rules.spec.ts unit da regra pura, sem double
|
|
14
|
+
services/<domain>-access.spec.ts matriz ação x permissão x estado
|
|
15
|
+
services/<domain>.fixtures-spec.ts ids fixos, fábricas de record, fakeRepository, fakePolicy
|
|
16
|
+
adapters/<tecnologia>.<domain>.repository.adapter.spec.ts tradução de constraint, montagem de SQL, contra banco real quando o repo tiver
|
|
17
|
+
common/utils/<tipo>/<nome>.<tipo>.unit.spec.ts helper puro
|
|
18
|
+
test/e2e/<assunto>.e2e.spec.ts app Nest inteira: guards, pipes, filter, rota, documento
|
|
19
|
+
libraries/test/ configs do runner por tipo (unit, integration, e2e)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
O sufixo `-spec` (sem ponto) não casa com o glob usual `*.spec.ts`: a fixture é importada pelos specs e não roda como suíte. Confira o glob do runner do repo antes de criar uma.
|
|
23
|
+
|
|
24
|
+
### Service spec
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { describe, expect, it } from 'vitest'; // runner do repo
|
|
28
|
+
import { instructions as createContract } from '../surfaces/create-invoice.invoices.props.surface';
|
|
29
|
+
import { ACTOR, CUSTOMER, ITEM, customerAccess, customerRecord, fakePolicy, fakeRepository } from './invoices.fixtures-spec';
|
|
30
|
+
import { CreateInvoiceService } from './create-invoice.service';
|
|
31
|
+
|
|
32
|
+
function setup(options: Parameters<typeof fakeRepository>[0] = {}, access = customerAccess(['invoices:create'])) {
|
|
33
|
+
const fake = fakeRepository(options);
|
|
34
|
+
const service = new CreateInvoiceService(fake.repository as never, fake.resolver, fakePolicy(access) as never);
|
|
35
|
+
return { ...fake, service };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('CreateInvoiceService', () => {
|
|
39
|
+
it('cria com audit e devolve o contrato', async () => {
|
|
40
|
+
const { service, repository } = setup();
|
|
41
|
+
const result = await service.execute({ actorId: ACTOR, customerId: CUSTOMER, items: [ITEM] });
|
|
42
|
+
expect(repository.createInvoice.mock.calls[0][0]).toMatchObject({ customer: CUSTOMER, createdBy: ACTOR });
|
|
43
|
+
const parsed = createContract.settings.response.body.safeParse(result);
|
|
44
|
+
expect(parsed.success, JSON.stringify(parsed.error?.issues)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('cliente bloqueado = 422 invoice.customer_blocked, sem escrita', async () => {
|
|
48
|
+
const { service, repository } = setup({ customer: customerRecord({ status: 'BLOCKED' }) });
|
|
49
|
+
await expect(service.execute({ actorId: ACTOR, customerId: CUSTOMER, items: [ITEM] }))
|
|
50
|
+
.rejects.toMatchObject({ response: { code: 'invoice.customer_blocked' } });
|
|
51
|
+
expect(repository.createInvoice).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- **Instancie o service com `new`** e os doubles da fixture; o teste não sobe o Nest.
|
|
57
|
+
- **Valide o retorno contra `instructions.settings.response.body`** em pelo menos um caso por service: é o teste de contrato mais barato que existe.
|
|
58
|
+
- **Assert por `code`** (`rejects.toMatchObject({ response: { code } })`), nunca por mensagem.
|
|
59
|
+
- Callbacks passados ao port (audits) são chamados no teste com um id fictício e afirmados.
|
|
60
|
+
- O double do port nasce na fixture do domínio; método novo no port entra na fixture no mesmo PR.
|
|
61
|
+
|
|
62
|
+
### Mínimo por endpoint
|
|
63
|
+
|
|
64
|
+
```txt
|
|
65
|
+
- [ ] service: caminho feliz + retorno válido contra o schema de resposta
|
|
66
|
+
- [ ] service: cada code novo, com o status certo, e sem escrita quando recusa
|
|
67
|
+
- [ ] access: quem pode, quem não pode, estado que bloqueia, 404 x 403
|
|
68
|
+
- [ ] rules: limites e casos de borda da regra pura
|
|
69
|
+
- [ ] adapter (se mudou SQL ou tradução): filtro ausente não filtra; sort fora da whitelist não chega ao SQL; constraint vira o code
|
|
70
|
+
- [ ] e2e de grupo crítico: 401 sem token, 403/404 sem acesso, 400 com campo extra (.strict()), sucesso
|
|
71
|
+
- [ ] multi-tenant: recurso de outro tenant não vaza
|
|
72
|
+
- [ ] sessão (se houver cache de sessão): revogação não autoriza com o cache fora
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 2. Domínio novo
|
|
78
|
+
|
|
79
|
+
1. **Inventário** do `SKILL.md` ("Antes de editar") mais: tabelas que o domínio lê e escreve, permissões existentes e novas, domínios de que depende e quem vai consumi-lo.
|
|
80
|
+
2. **Plano mínimo**, antes do código:
|
|
81
|
+
|
|
82
|
+
```txt
|
|
83
|
+
1. Recursos e endpoints, com o template de cada um (endpoints.md)
|
|
84
|
+
2. Contrato de cada endpoint: status, security, permissão, codes de 409/422
|
|
85
|
+
3. Schemas de entities (params, filtros, sort, item, detalhe)
|
|
86
|
+
4. Services, rules e access; o que vai para *-context.ts e *.resolver.ts
|
|
87
|
+
5. Port e adapter; o que é leitura quente em SQL; transações e constraints
|
|
88
|
+
6. Tabelas, índices e migrations com arch-db; permissões novas no catálogo e na seed
|
|
89
|
+
7. Idempotência, outbox, rate limit, timeout de provider
|
|
90
|
+
8. Testes por camada
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
3. **Scaffold**, se o repo tiver, com as condições do passo 1 de "Criando um endpoint" no `SKILL.md`; substitua cada stub pelo padrão dos vizinhos.
|
|
94
|
+
4. **`doc.md`** nasce junto, com este esqueleto:
|
|
95
|
+
|
|
96
|
+
```md
|
|
97
|
+
# <Domain> Module
|
|
98
|
+
|
|
99
|
+
## Overview
|
|
100
|
+
O que o domínio faz e o que não é dele (com o módulo dono de cada vizinho).
|
|
101
|
+
|
|
102
|
+
## Status
|
|
103
|
+
| Endpoint | Rota | Permissão |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `listCustomerInvoices` | `GET /customers/:customerId/invoices` | ler o cliente |
|
|
106
|
+
|
|
107
|
+
## Autorização (`services/<domain>-access.ts`)
|
|
108
|
+
Policy usada, mapa de ações, visibilidade (o que vira 404), estados que bloqueiam escrita.
|
|
109
|
+
|
|
110
|
+
## Regras (puras, testadas)
|
|
111
|
+
Um item por `*.rules.ts`: o que decide e os codes que lança.
|
|
112
|
+
|
|
113
|
+
## Escrita
|
|
114
|
+
Transações, lock otimista, sequências, audits gravados.
|
|
115
|
+
|
|
116
|
+
## Persistência
|
|
117
|
+
Port -> adapter; o que é SQL (`<domain>.sql.ts`) e o que é ORM; constraints traduzidas.
|
|
118
|
+
|
|
119
|
+
## Depois
|
|
120
|
+
Pendências e dívidas conhecidas.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 3. Migração de projeto existente (ondas)
|
|
126
|
+
|
|
127
|
+
**Onda 1, contrato e client**: `operationId` camelCase e estável; schemas nomeados com `register`; `security` = guard; datas ISO; blocos de erro só onde ocorrem; `code` em todo 409/422.
|
|
128
|
+
|
|
129
|
+
**Onda 2, exec fino**: `Zd*` com `instructions`; tipos `Instruction.*`; regra, SQL e try/catch saem do controller para o service; um service por caso de uso.
|
|
130
|
+
|
|
131
|
+
**Onda 3, domínio e persistência**: access por domínio; port com tipos escritos à mão no lugar do repositório genérico do scaffold; transação dentro do adapter; `-db-errors` com mapa de constraints; SQL parametrizado com sort por mapa.
|
|
132
|
+
|
|
133
|
+
**Onda 4, segurança e plataforma**: TTL e revogação de sessão; backdoors removidos; CORS, limite de body, `trust proxy` e docs fail-closed; env completo no schema; exceções de domínio no lugar das do Nest.
|
|
134
|
+
|
|
135
|
+
**Onda 5, governança**: `openapi:generate` + `git diff --exit-code` no CI; e2e por grupo crítico; idempotência e timeout/retry onde faltam; `doc.md` em todo domínio.
|
|
136
|
+
|
|
137
|
+
Melhoria localizada, sem ADR:
|
|
138
|
+
|
|
139
|
+
- **Rota pública marcada como pública no documento.** Onde o vizinho público só omite `security`, a operação herda o `security` global do build e o documento a mostra como protegida: acrescente `security: []` no `registerPath` (e corrija o template do scaffold, se ele gerar a omissão).
|
|
140
|
+
|
|
141
|
+
Melhorias que mudam o padrão e pedem ADR antes:
|
|
142
|
+
|
|
143
|
+
- **Centralizar o path do exec.** Hoje o path é literal repetido no decorator. Alternativa: derivar do props (`@Controller(instructions.settings.endpoint)` com subrotas declaradas no contrato) ou um decorator que aplica `instructions` ao método. Muda o scaffold e todas as surfaces; enquanto não muda, a trava é a paridade ou o e2e.
|
|
144
|
+
- **Checagem de paridade rota x documento** no gate, quando o repo não tem: lista as rotas registradas no Nest e compara com as operações do `openapi.json`.
|
|
145
|
+
- **Correlação antes dos guards.** Gerar o `traceId` num middleware para que 401, 403 e rate limit também saiam com ele (`security.md` §8).
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 4. ADR
|
|
150
|
+
|
|
151
|
+
Crie ADR quando a decisão afeta mais de um endpoint, módulo ou time:
|
|
152
|
+
|
|
153
|
+
```txt
|
|
154
|
+
mudar envelope global ou formato de erro (inclusive o formato do code)
|
|
155
|
+
mudar auth, JWT ou estratégia de sessão
|
|
156
|
+
introduzir multi-tenancy ou mudar como o tenant é resolvido
|
|
157
|
+
trocar ORM, driver ou a divisão escrita x leitura entre eles
|
|
158
|
+
introduzir outbox, fila ou estratégia de eventos
|
|
159
|
+
mudar paginação padrão ou versionamento
|
|
160
|
+
mudar o scaffold ou a forma de props/exec
|
|
161
|
+
expor a API para consumidor externo
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Se o repo tem `docs/adr` (ou equivalente), siga o formato e a numeração de lá. Senão, template mínimo:
|
|
165
|
+
|
|
166
|
+
```md
|
|
167
|
+
# ADR: <título>
|
|
168
|
+
|
|
169
|
+
## Contexto
|
|
170
|
+
## Decisão
|
|
171
|
+
## Alternativas consideradas
|
|
172
|
+
## Consequências positivas
|
|
173
|
+
## Consequências negativas
|
|
174
|
+
## Plano de migração
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Alternativas, cada uma com o motivo real do descarte, dão valor ao ADR meses depois. Consequências negativas são obrigatórias: ADR sem custo declarado é justificativa, não decisão.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 5. Scorecard de PR
|
|
182
|
+
|
|
183
|
+
Nota mínima recomendada 8/10. Mudança crítica (auth, permissões, migration, contrato público) busca 9+.
|
|
184
|
+
|
|
185
|
+
| Critério | Peso | Pergunta |
|
|
186
|
+
|---|---:|---|
|
|
187
|
+
| Contrato | 2 | props e exec batem? schemas nomeados? `code` de 409/422 declarados? documento regenerado? |
|
|
188
|
+
| Arquitetura | 1 | exec fino, um service por caso de uso, access no domínio, port sem tipo do ORM, transação no adapter? |
|
|
189
|
+
| Segurança | 2 | guard = `security`, access antes da escrita, tenant, `.strict()`, rate limit, nada sensível no log? |
|
|
190
|
+
| Performance | 1 | lista com teto e tiebreaker, sort por mapa, sem N+1, payload limitado, timeout? |
|
|
191
|
+
| Observabilidade | 1 | erro com `code` estável, log estruturado, audit em write sensível? |
|
|
192
|
+
| Testes | 1 | service com contrato e cada `code`, rules, access, e2e do grupo crítico? |
|
|
193
|
+
| Compatibilidade | 1 | breaking change classificado, inclusive mudança de `code`? |
|
|
194
|
+
| Operação | 1 | idempotência, outbox, retry e falha de provider avaliados? `doc.md` atualizado? |
|
|
195
|
+
|
|
196
|
+
```txt
|
|
197
|
+
0-6 não aprovar
|
|
198
|
+
7 aprovar só se baixo risco e com dívida registrada
|
|
199
|
+
8 bom, aprovar com pequenos ajustes
|
|
200
|
+
9 padrão forte para produção
|
|
201
|
+
10 referência para outros domínios
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 6. Checklist final antes de entregar
|
|
207
|
+
|
|
208
|
+
```txt
|
|
209
|
+
- [ ] O domínio segue a estrutura e os sufixos dos vizinhos?
|
|
210
|
+
- [ ] Não inventei helper, decorator, guard ou exceção que já existia em common/?
|
|
211
|
+
- [ ] props e exec batem (path, verbo, status, security, schemas)?
|
|
212
|
+
- [ ] Todo schema que vira tipo no client tem nome e register?
|
|
213
|
+
- [ ] O access autoriza o recurso antes de qualquer escrita?
|
|
214
|
+
- [ ] Tenant isolado em toda query?
|
|
215
|
+
- [ ] A resposta passa pelo mapper e expõe só o necessário?
|
|
216
|
+
- [ ] Lista com teto, sort por mapa, tiebreaker e meta pelo helper?
|
|
217
|
+
- [ ] Multi-write em uma transação no adapter, sem chamada externa dentro?
|
|
218
|
+
- [ ] Toda constraint que o write pode violar vira code?
|
|
219
|
+
- [ ] Provider externo atrás de port, com timeout?
|
|
220
|
+
- [ ] Testes do mínimo por endpoint criados ou atualizados?
|
|
221
|
+
- [ ] openapi:generate, check-types, lint e testes verdes?
|
|
222
|
+
- [ ] Breaking change classificado?
|
|
223
|
+
- [ ] Migration revisada com arch-db?
|
|
224
|
+
- [ ] doc.md atualizado?
|
|
225
|
+
- [ ] Comandos do package.json usados, não inventados?
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Item que não se aplica: registre como "não aplicável" com o motivo técnico.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Segurança, erros e operação
|
|
2
|
+
|
|
3
|
+
Abra ao mexer em autenticação, sessão, guards, rate limit, tenant, configuração do bootstrap, formato de erro, logs ou integração externa, e ao revisar um endpoint contra o checklist OWASP. As regras curtas estão no `SKILL.md` (regras 7 a 9 e 15); onde cada peça mora está em `common.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Configuração fail-closed
|
|
8
|
+
|
|
9
|
+
- **Docs só com literal `'true'` e fora de produção**: `OPENAPI_ENABLED === 'true' && <modo> !== <produção>`. A forma `!== 'false'` faz o contrário: um deploy que esquece a flag expõe a superfície inteira. Compare o modo **normalizado** (o schema de env normaliza aliases, `common.md` §3); comparar o `process.env` cru com um alias deixa os docs ligados quando o valor vem na forma canônica.
|
|
10
|
+
- **CORS por lista explícita** vinda do env (separada por vírgula), com `credentials: true`; nunca `origin: '*'` com credentials. CORS aberto numa API de autenticação transforma um XSS em qualquer domínio em roubo de sessão.
|
|
11
|
+
- **`trust proxy` pelo parser do repo, default loopback.** Configure só os saltos que são seus (número de saltos ou sub-redes). `true` confia na cadeia inteira: o cliente forja `X-Forwarded-For`, escapa do rate limit e falsifica o IP do log.
|
|
12
|
+
- **Limite de body explícito** no bootstrap. Upload grande vai por rota ou fluxo próprio (capacidade de storage), não aumentando o limite global.
|
|
13
|
+
- **Segredo com tamanho mínimo validado no schema.** Segredo curto assina igualmente bem e é quebrável offline. Nenhum segredo em arquivo versionado; `.env.example` só com placeholder.
|
|
14
|
+
- `x-powered-by` desligado; `helmet` ligado (CSP desligada só se a API não serve HTML próprio além da UI de docs).
|
|
15
|
+
- Comportamento sem uma configuração opcional é decidido e escrito no `describe` da env: fail-open só onde a falha não abre acesso; o resto, fail-closed ou flag explícita.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. Sessão e token
|
|
20
|
+
|
|
21
|
+
- Access token com TTL curto; refresh com teto absoluto de sessão (o refresh não estende além dele). Os valores vêm do env, com `.positive()`.
|
|
22
|
+
- O refresh **rotaciona** a cada uso; reapresentar um refresh já usado fora de uma janela de graça curta é tratado como roubo e revoga todas as sessões do usuário. É contrato: os clients renovam num ponto único, com dedupe de concorrentes (`arch-web`: `proxy.ts`; `arch-app`: `authedRequest`). Mudar a janela ou a regra pede ADR.
|
|
23
|
+
- Quando a reação esperada do client é diferente (renovar token x fazer login de novo), o 401 distingue os dois casos de forma estável e documentada; um 401 único obriga o client a adivinhar e desloga quem só precisava renovar. Se o shape de 401 do repo não tem `code`, registre a lacuna: acrescentar `code` muda o formato de erro e pede ADR (`review.md` §4).
|
|
24
|
+
- **Revogação nunca fica fail-open por indisponibilidade de cache.** Se a validade da sessão é lida de um cache na frente do banco, miss, timeout ou cache fora caem no banco; não existe caminho em que a ausência do cache autorize uma sessão revogada. Teste dedicado obrigatório.
|
|
25
|
+
- Se o repo versiona sessão ou permissão no token, toda mudança de acesso invalida o token corrente; endpoint que grava permissão ou vínculo sem invalidar deixa acesso revogado valendo até o token expirar.
|
|
26
|
+
- Login com provedor externo valida o `aud` contra uma lista do env; sem a lista, o login falha fechado.
|
|
27
|
+
- **Proibido, sem exceção**: senha mestra, coringa ou backdoor (qualquer código no caminho de autenticação que aceite valor fixo no lugar do hash) e comparação de senha fora do provider de hash. O caminho legítimo é impersonation auditada, com permissão própria e escopo limitado.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 3. Guards e autorização
|
|
32
|
+
|
|
33
|
+
O guard autentica e limita taxa; a autorização por recurso é do access (regra 9 do `SKILL.md`, mecânica em `domain.md` §3), e o adapter só filtra a leitura pelo `viewer` que o access produziu.
|
|
34
|
+
|
|
35
|
+
- Por que sem guard global de autenticação (regra 8 do `SKILL.md`): ele obriga rotas públicas a se marcarem com `@Public()`, e esquecer o decorator numa rota nova só aparece em produção.
|
|
36
|
+
- `AuthUserGuard` estende o guard do passport com a estratégia JWT de `common/auth/`; `OptionalAuthUserGuard` deixa passar sem token e entrega o usuário quando há.
|
|
37
|
+
- **Rate limit por guards dedicados** (na referência, um para rotas de autenticação e um para escrita). Rota de login, cadastro, recuperação de senha, busca, exportação, upload e integração recebe o guard que o vizinho equivalente usa. Se o repo tem throttling global, mantenha-o e ajuste a janela por rota sensível. Storage em memória conta por réplica (teto efetivo = N x configurado); quando precisar ser exato, troque por storage compartilhado sem tocar em controller.
|
|
38
|
+
- Guard novo só quando a regra é transversal e HTTP-aware. Guard com configuração é fábrica com parâmetro obrigatório; nunca `SetMetadata` + `Reflector`, porque quem esquece a metadata anexa um guard que lê `undefined` e libera a request.
|
|
39
|
+
- O 403 **não diz qual permissão faltou**: isso ensina a superfície de permissões a quem sonda. A lista requerida vai só para o log.
|
|
40
|
+
- Catálogo de chaves de permissão e onde entra uma permissão nova: `domain.md` §3.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 4. Contexto HTTP
|
|
45
|
+
|
|
46
|
+
IP, user agent, id de correlação, idempotency key e referência de tenant são extraídos por decorators (`contract.md` §4) para o service receber dado, não o `Request`. Decorator de contexto opcional devolve `undefined` quando não dá para afirmar o valor (header malformado não derruba a request com 500). Se persistir IP, normalize IPv4 mapeado em IPv6 (`::ffff:10.0.0.5` -> `10.0.0.5`).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 5. Multi-tenancy
|
|
51
|
+
|
|
52
|
+
```txt
|
|
53
|
+
- tenant de operação sensível nunca vem só do body
|
|
54
|
+
- a referência do tenant ativo vem de fonte confiável (token, ou header lido por @<Tenant>Ref() que nunca lança)
|
|
55
|
+
- o service resolve a referência por um resolver que recusa tenant inexistente, removido ou cancelado
|
|
56
|
+
- o access valida ator + tenant + recurso; tenant encerrado não aceita escrita (403)
|
|
57
|
+
- toda query sensível filtra pelo tenant; buscar só por id é vazamento
|
|
58
|
+
- testes cobrem acesso cross-tenant
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// ruim: recurso só por id em sistema multi-tenant
|
|
63
|
+
repository.findInvoice(invoiceId)
|
|
64
|
+
// bom: id + tenant resolvido do contexto confiável
|
|
65
|
+
repository.findInvoice(tenant.id, invoiceId)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
RLS, índice com a coluna de tenant e FK composta: `arch-db`.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 6. Checklist OWASP por endpoint
|
|
73
|
+
|
|
74
|
+
- **BOLA**: o access confere o recurso específico, não só a permissão funcional.
|
|
75
|
+
- **BFLA**: a ação está no mapa `<DOMAIN>_ACTIONS` com as permissões certas.
|
|
76
|
+
- **Tenant isolation**: nunca confiar só no id de tenant enviado pelo cliente.
|
|
77
|
+
- **Mass assignment**: regra 4 do `SKILL.md`, e o schema de input não declara campos internos (`role`, `status`, `tenant`, `createdBy`) sem regra explícita.
|
|
78
|
+
- **Excessive data exposure**: resposta pelo mapper, só o necessário.
|
|
79
|
+
- **Abuso**: rate limit, `Idempotency-Key` e limite de payload avaliados.
|
|
80
|
+
|
|
81
|
+
```txt
|
|
82
|
+
AuthN [ ] público ou protegido explícito? [ ] guard runtime = security do contrato?
|
|
83
|
+
AuthZ [ ] access chamado antes da escrita? [ ] tenant e estado validados?
|
|
84
|
+
[ ] actorId vem do token, nunca do body? [ ] mudança de acesso invalida o token corrente?
|
|
85
|
+
Input [ ] .strict() em params, query e body? [ ] texto e arrays com .max()? [ ] update campo a campo?
|
|
86
|
+
Output [ ] sem hash, token, causa técnica ou PII de terceiro?
|
|
87
|
+
Abuso [ ] rate limit cobre? [ ] idempotência e replay avaliados? [ ] 403/404 não vira oráculo de existência?
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 7. Erros
|
|
93
|
+
|
|
94
|
+
Shape em `common/schemas/errors.ts`:
|
|
95
|
+
|
|
96
|
+
| Status | Corpo |
|
|
97
|
+
|---|---|
|
|
98
|
+
| 400 | `{ statusCode, message, errors?, traceId? }`; `errors` traz o detalhe da validação do pipe |
|
|
99
|
+
| 401, 403, 404 | `{ statusCode, message, traceId? }` |
|
|
100
|
+
| 409 | `{ statusCode, message, code?, traceId? }`; `code` quando a operação informa (ex.: `invoice.stale`) |
|
|
101
|
+
| 422 | `{ statusCode, message, code, details?, traceId? }`; `code` obrigatório |
|
|
102
|
+
| 500 | mensagem genérica; nada de causa |
|
|
103
|
+
|
|
104
|
+
- O client decide comportamento por `code` (e por status), nunca por `message`; `message` é para humano, no idioma do produto, e pode mudar. Por isso mudar um `code` é major (`contract.md` §8).
|
|
105
|
+
- `details` carrega só o que a tela precisa para reagir (campos faltando, campo e motivo); causa técnica vai para o log.
|
|
106
|
+
- Lance exceções de domínio de `common/http/errors` (`common.md` §4): elas carregam o corpo no shape acima. Exceção nova (outro status ou campo) muda a exceção, o schema de erro e o bloco de `response.docs.ts` juntos, com teste do shape.
|
|
107
|
+
- Erro do banco passa por `<domain>-db-errors` e `rethrowDbError` (`domain.md` §6): nunca SQL, stack, nome de tabela ou constraint, nem valor de outra pessoa no response.
|
|
108
|
+
- 4xx é esperado e documentado; 5xx é genérico para o cliente e detalhado só no log.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 8. Observabilidade
|
|
113
|
+
|
|
114
|
+
O interceptor de correlação lê o header de correlação (ou gera um id), mede a duração e loga o desfecho; o filter de exceções do repo (na referência, um logger de exceções HTTP) cobre o que o interceptor não vê.
|
|
115
|
+
|
|
116
|
+
```txt
|
|
117
|
+
http_request_completed traceId, method, route (template), statusCode, durationMs, actorId
|
|
118
|
+
http_request_failed os mesmos + errorName, error, statusCode, response da exceção, cause { code, message, meta }, stack (primeiras linhas)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- **Ordem do Nest**: middleware -> guards -> interceptors -> pipes -> handler. O interceptor envolve pipes e handler (vê o 400 de validação), mas não roda quando um guard recusa: 401/403 e rate limit dependem do filter para log e não ganham o `traceId` do interceptor. Item de revisão quando o suporte precisar correlacionar essas recusas: gerar o id num middleware (`review.md` §3).
|
|
122
|
+
- Log estruturado (JSON), nunca texto solto como única fonte; `tenant` no log quando permitido.
|
|
123
|
+
- **Nunca logar**: senha, token, refresh token, `Authorization`, cookie de sessão, segredo, documento sensível, cartão, payload confidencial completo, resposta integral de provider com PII.
|
|
124
|
+
- Propague o id de correlação para adapters externos quando o provider aceitar.
|
|
125
|
+
- Métricas e tracing, se o repo tiver: contagem e duração por operação e status, erros por `code`, latência e erro por provider externo; nenhum segredo em atributo de span.
|
|
126
|
+
- Write sensível grava audit (`actorId`, ação, entidade, `previous`, `current`, `metadata`) na mesma transação (`domain.md` §1). Audit não substitui log técnico nem evento de domínio.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 9. Resiliência de integrações externas
|
|
131
|
+
|
|
132
|
+
O service não conhece HTTP client, URL, header, SDK ou payload do provider: ele injeta um port (`common/<capacidade>` ou `ports/<x>.provider.port.ts` no domínio).
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
export abstract class IPaymentProvider {
|
|
136
|
+
abstract createPayment(input: CreatePaymentInput): Promise<CreatePaymentOutput>;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Todo adapter externo declara a política antes do merge:
|
|
141
|
+
|
|
142
|
+
```txt
|
|
143
|
+
Provider: Operação: Timeout: Retry: Backoff: Circuit breaker:
|
|
144
|
+
Idempotente?: Erros mapeados: Métricas/logs: Fallback:
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- **Timeout explícito em toda chamada externa.**
|
|
148
|
+
- Retry só em erro transitório e operação idempotente; nunca retry cego em POST não idempotente. Não retentar 400/401/403/404; avaliar 408/429/5xx com backoff e teto.
|
|
149
|
+
- Circuit breaker para provider instável ou crítico.
|
|
150
|
+
- Chamada ao provider fora da transação (regra 14 do `SKILL.md`); quando o efeito não pode se perder, outbox (`domain.md` §9).
|
|
151
|
+
- Erro de provider não vaza cru: timeout -> 504 ou erro de domínio; indisponível -> 503; validação -> 422/400; conflito -> 409; sempre com `code` quando o client precisa reagir.
|