feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# 📚 Biblioteca Inteligente — Índice Completo
|
||||
|
||||
> Biblioteca dinâmica para compartilhar entre todos os projetos. Conhecimento extraído
|
||||
> automaticamente das skills instaladas, organizado por domínio.
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Estrutura
|
||||
|
||||
```
|
||||
libs/
|
||||
├── README.md ← Este arquivo
|
||||
├── typescript/ ← TS seguro: narrowing, generics, satisfies
|
||||
├── react/ ← Next.js, Vite, App Router, RSC, Server Actions
|
||||
├── vue/ ← Vue 3 Composition API, Pinia, Router
|
||||
├── linux/ ← Diagnóstico de sistema, logs, rede, SSH
|
||||
├── database/ ← PostgreSQL, MySQL — schemas, queries, EXPLAIN
|
||||
├── browser/ ← Agent-browser (Chromium) + E2E testing
|
||||
├── security/ ← SAST: OWASP Top 10, prompt injection, secrets
|
||||
├── best-practices/ ← Clean Code, SOLID, Clean Architecture
|
||||
├── deploy/ ← Docker multi-stack, xCloud, OpenClaw Gateway
|
||||
└── docs/ ← Templates de documentação
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Typescript — 2 arquivos
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `typescript/TYPESCRIPT_SAFE_PATTERNS.md` | narrowing, satisfies, discriminated unions, literais |
|
||||
| `typescript/GENERICS_UTILITY_GOTCHAS.md` | Armadilhas de generics & utility types `Partial<T>`, `Omit`, `Pick` |
|
||||
|
||||
## 📁 React — 2 arquivos
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `react/NEXTJS_BEST_PRACTICES.md` | App Router, RSC, Server Actions, caching, data fetching |
|
||||
| `react/VITE_CONFIG.md` | Env vars, path aliases, Dev Server proxy, CJS compat |
|
||||
|
||||
## 📁 Vue — 1 arquivo
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `vue/VUE3_COMPOSITION_API.md` | `<script setup>`, composables, Pinia, reactivity, Router traps |
|
||||
|
||||
## 📁 Linux — 1 arquivo
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `linux/SYSTEM_DIAGNOSTIC.md` | Processes, disk, network, logs, SSH, shell scripts, permissions |
|
||||
|
||||
## 📁 Database — 2 arquivos
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `database/POSTGRESQL.md` | UUID, JSONB, GIN indexes, CTEs, window functions, EXPLAIN ANALYZE |
|
||||
| `database/MYSQL.md` | AUTO_INCREMENT, JSON, InnoDB, diferenças vs PostgreSQL |
|
||||
|
||||
## 📁 Browser — 2 arquivos
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `browser/BROWSER_AUTOMATION.md` | Chromium: navegação, login, persists, screenshot, @refs, dev server proxy |
|
||||
| `browser/E2E_TESTING.md` | Pirâmide de testes, selectors estáveis, princípios de determinismo |
|
||||
|
||||
## 📁 Security — 1 arquivo
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `security/SAST_AUDIT.md` | OWASP Top 10, prompt injection, hardcoded secrets, IDOR, SSRF |
|
||||
|
||||
## 📁 Best Practices — 1 arquivo
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `best-practices/CLEAN_CODE.md` | Naming, functions, SRP, SOLID, Clean Architecture dependency rule |
|
||||
|
||||
## 📁 Deploy — 2 arquivos
|
||||
| Arquivo | Conteúdo |
|
||||
|---------|----------|
|
||||
| `deploy/DOCKER_DEPLOY.md` | Stack router, 3 cenários, Dockerfiles pré-prontos, composes template |
|
||||
| `deploy/OPENCLAW_OPS.md` | CLI cheatsheet, $include modular, Telegram, env vars |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Como Usar
|
||||
|
||||
### Em cada novo projeto:
|
||||
1. Copiar `libs/` → `projeto/docs/dev-standards/`
|
||||
2. Selecionar pastas relevantes ao stack
|
||||
3. Consulte antes de implementar cada camada
|
||||
|
||||
### Quando o agente codifica:
|
||||
1. `libs/best-practices/CLEAN_CODE.md` — sempre primeiro
|
||||
2. `libs/<stack>/` — padrões específicos
|
||||
3. Aplica automaticamente — não precisa lembrar de cor
|
||||
|
||||
### Quando atualizar:
|
||||
- Nova skill instalada → ler SKILL.md → extrair conhecimento → atualizar `libs/`
|
||||
- Após cada projeto → feedback → atualizar padrões
|
||||
@@ -0,0 +1,120 @@
|
||||
# Clean Code & Architecture — Uncle Bob + SOLID
|
||||
|
||||
> Extracted from: `uncle-bob` v1.0.0 + `clean-code-review` v1.0.0
|
||||
|
||||
## 🧹 Clean Code — Regras Fundamentais
|
||||
|
||||
### Naming (nomes são a melhor documentação)
|
||||
| Elemento | Convenção | ❌ Errado | ✅ Certo |
|
||||
|----------|-----------|-----------|--------|
|
||||
| Variáveis | Revelam intenção | `n`, `d`, `tmp` | `userCount`, `elapsed` |
|
||||
| Funções | Verbo + substantivo | `user()`, `calc()` | `getUserById()`, `calculateTotal()` |
|
||||
| Booleanos | Forma de pergunta | `active`, `flag` | `isActive`, `hasPermission`, `canEdit()` |
|
||||
| Constantes | SNAKE_CASE | `max`, `timeout` | `MAX_RETRY_COUNT`, `REQUEST_TIMEOUT_MS` |
|
||||
| Classes | Substantivo | `Manager`, `Data` | `UserRepository`, `OrderService` |
|
||||
|
||||
> Se precisa de comentário para explicar o nome, o nome está errado.
|
||||
|
||||
### Functions — Regras
|
||||
| Regra | Guia |
|
||||
|-------|------|
|
||||
| **Small** | Máx 20 linhas, ideal 5-10 |
|
||||
| **One Thing** | Faz uma coisa, e faz bem |
|
||||
| **One Level** | Um nível de abstração por função |
|
||||
| **Few Args** | Max 3 argumentos, preferir 0-2 |
|
||||
| **No Side Effects** | Não muta entradas inesperadamente |
|
||||
|
||||
### Guard Clauses (evitar ninho profundidade)
|
||||
```ts
|
||||
// ❌ 5 níveis de profundidade
|
||||
function processOrder(order: Order) {
|
||||
if (order) {
|
||||
if (order.items.length > 0) {
|
||||
if (order.total > 0) { ... }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Guard clauses — zero aninhamento
|
||||
function processOrder(order: Order) {
|
||||
if (!order) return;
|
||||
if (order.items.length === 0) return;
|
||||
if (order.total <= 0) return;
|
||||
// lógica principal aqui
|
||||
}
|
||||
```
|
||||
|
||||
### SRP — Single Responsibility
|
||||
> Uma classe tem **um** motivo para mudar. Um ator, uma responsabilidade.
|
||||
|
||||
### DRY + YAGNI
|
||||
- **DRY**: Não duplique — três instâncias de duplicação é o limite para abstrair
|
||||
- **YAGNI**: Você não vai precisar — não construa features não requisitadas
|
||||
|
||||
---
|
||||
|
||||
## 🔷 SOLID Principles
|
||||
|
||||
| Princípio | Regra Curta |
|
||||
|-----------|-------------|
|
||||
| **S** — Single Responsibility | Uma razão para mudar |
|
||||
| **O** — Open/Closed | Aberto para extensão, fechado para modificação |
|
||||
| **L** — Liskov Substitution | Subtipo deve ser substituível pelo seu tipo base |
|
||||
| **I** — Interface Segregation | Muitas interfaces específicas > uma geral |
|
||||
| **D** — Dependency Inversion | Dependa de abstrações, não de concretudes |
|
||||
|
||||
### D — Dependency Inversion Exemplo
|
||||
```ts
|
||||
// ❌ Alto nível depende de baixo nível
|
||||
class ReportGenerator {
|
||||
private db = new MySQLConnection(); // concreto!
|
||||
}
|
||||
|
||||
// ✅ Depende de abstração
|
||||
interface Database { query(sql: string): Promise<any[]> }
|
||||
class ReportGenerator {
|
||||
constructor(private db: Database) {} // injetado!
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Clean Architecture — Dependency Rule
|
||||
|
||||
> Dependências de código apontam **para dentro** — para políticas de mais alto nível.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Entities / Domain │ ← Nada depende disto
|
||||
├─────────────────────────────────────┤
|
||||
│ Use Cases / Business Logic │ ← Só depende de Entities
|
||||
├─────────────────────────────────────┤
|
||||
│ Interface Adapters (Controllers) │ ← Só depende de Use Cases
|
||||
├─────────────────────────────────────┤
|
||||
│ Frameworks & Drivers (DB, UI) │ ← Tudo depende para dentro
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Regra da Dependência
|
||||
```
|
||||
Código de alto nível → abstração → código de baixo nível
|
||||
(não pode depender de detalhes)
|
||||
```
|
||||
|
||||
### Quando Usar Clean Architecture
|
||||
- Projetos com vida útil esperada > 2 anos
|
||||
- Múltiplas equipes trabalhando no mesmo código
|
||||
- Possibilidade de trocar framework
|
||||
- Regras de negócio complexas
|
||||
|
||||
## Code Review Checklist (Clean Code)
|
||||
- [ ] Nomes revelam intenção?
|
||||
- [ ] Funções fazem uma coisa só?
|
||||
- [ ] Sem comentários que só recontam o que o código faz?
|
||||
- [ ] Sem código comentado fora?
|
||||
- [ ] Sem mais de 3 argumentos em qualquer função?
|
||||
- [ ] SRP respeitado?
|
||||
- [ ] DRY aplicado mas sem over-abstração?
|
||||
- [ ] Tratamento de erros centralizado?
|
||||
- [ ] Sem uso de `any`/`as any` sem justificativa?
|
||||
- [ ] O retorno do commit deixa o código melhor do que encontrou? (Boy Scout)
|
||||
@@ -0,0 +1,91 @@
|
||||
# Browser Automation — Chromium CLI (agent-browser)
|
||||
|
||||
> CLI headless Chromium para navegação, login, raspagem e screenshots.
|
||||
|
||||
## Fluxo Padrão
|
||||
```bash
|
||||
agent-browser open https://exemplo.com/form
|
||||
agent-browser snapshot -i # Descobre elementos → @e1, @e2, @e3
|
||||
agent-browser fill @e1 "user@exemplo.com"
|
||||
agent-browser fill @e2 "senha123"
|
||||
agent-browser click @e3
|
||||
agent-browser wait --load networkidle
|
||||
agent-browser snapshot -i # Re-snapshot após mudança de DOM
|
||||
```
|
||||
|
||||
## Comandos Essenciais
|
||||
```bash
|
||||
# Navegação
|
||||
agent-browser open <url>
|
||||
agent-browser close
|
||||
|
||||
# Snapshot (sempre primeiro)
|
||||
agent-browser snapshot -i # Elementos interativos + @refs
|
||||
agent-browser snapshot -i -C # + cursor-interactive elements
|
||||
|
||||
# Interação com @refs
|
||||
agent-browser click @e1
|
||||
agent-browser fill @e2 "text" # Clear + type
|
||||
agent-browser type @e2 "text" # Type sem limpar
|
||||
agent-browser select @e1 "option"
|
||||
agent-browser check @e1
|
||||
agent-browser press Enter
|
||||
agent-browser scroll down 500
|
||||
agent-browser hover @e1
|
||||
|
||||
# Extração
|
||||
agent-browser get text @e1
|
||||
agent-browser get text body > page.txt
|
||||
agent-browser get html @e1
|
||||
agent-browser get url
|
||||
agent-browser get title
|
||||
|
||||
# Espera
|
||||
agent-browser wait @e1
|
||||
agent-browser wait --load networkidle
|
||||
agent-browser wait --url "**/dashboard"
|
||||
|
||||
# Captura
|
||||
agent-browser screenshot page.png
|
||||
agent-browser screenshot --full # Página inteira
|
||||
agent-browser pdf relatorio.pdf
|
||||
|
||||
# Download
|
||||
agent-browser download @e1 ./file.pdf
|
||||
agent-browser wait --download ./output.zip
|
||||
```
|
||||
|
||||
## 🔐 Login + Persistência de Sessão
|
||||
```bash
|
||||
# Login
|
||||
agent-browser open https://app.exemplo.com/login
|
||||
agent-browser fill @e1 "$USERNAME" && agent-browser fill @e2 "$PASSWORD"
|
||||
agent-browser click @e3
|
||||
agent-browser wait --url "**/dashboard"
|
||||
agent-browser state save ~/auth.json # Salva cookies + sessão
|
||||
|
||||
# Reutilizar
|
||||
agent-browser state load ~/auth.json
|
||||
agent-browser open https://app.exemplo.com/dashboard
|
||||
```
|
||||
|
||||
## 🔒 Segurança (ambiente)
|
||||
```bash
|
||||
export AGENT_BROWSER_ALLOWED_DOMAINS="exemplo.com"
|
||||
export AGENT_BROWSER_MAX_OUTPUT=50000
|
||||
export AGENT_BROWSER_CONTENT_BOUNDARIES=1
|
||||
```
|
||||
|
||||
## ⚠️ Ref Lifecycle
|
||||
> Refs (`@e1`, `@e2`) são **invalidados** após qualquer mudança de DOM.
|
||||
> Sempre faça `snapshot -i` de novo após:
|
||||
> - Cliques que navegam
|
||||
> - Submissão de formulário
|
||||
> - Conteúdo dinâmico (dropdowns, modais)
|
||||
|
||||
## Sessões Paralelas
|
||||
```bash
|
||||
agent-browser --session site1 open https://site-a.com
|
||||
agent-browser --session site2 open https://site-b.com
|
||||
agent-browser session list
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# E2E Testing — Pirâmide e Padrões
|
||||
|
||||
> Extraído de `e2e-testing-patterns` v1.0.0
|
||||
|
||||
## 🏔️ Test Pyramid
|
||||
```
|
||||
/\ ← POUCOS: Critical paths (E2E)
|
||||
/E2E\ ← MAIS: Component/API integration
|
||||
/─────\
|
||||
/Integr\ ← MUITOS: Unit tests (rápidos, isolados)
|
||||
/────────\
|
||||
/Unit Tests\
|
||||
/────────────\
|
||||
```
|
||||
|
||||
## O que E2E Tests DEVEM cobrir
|
||||
| ✅ E2E | ❌ Não E2E |
|
||||
|--------|-----------|
|
||||
| Critical journeys (auth → dashboard → logout) | Lógica unitária → use unit tests |
|
||||
| Multi-step flows (checkout, onboarding) | API contracts → use integration |
|
||||
| Cross-browser | Edge cases (muito lento) |
|
||||
| Real API integration | Internal implementation |
|
||||
| Auth flows | Visual states → use Storybook |
|
||||
|
||||
## Core Principles
|
||||
| Princípio | Como |
|
||||
|-----------|------|
|
||||
| **Test behavior, not implementation** | Assert em outcomes visíveis, não estrutura DOM |
|
||||
| **Independent tests** | Cada teste cria próprios dados e limpa |
|
||||
| **Deterministic waits** | Esperar por condição, não timeout fixo |
|
||||
| **Stable selectors** | Usar `data-testid`, roles, labels — nunca CSS classes |
|
||||
| **Fast feedback** | Mockar serviços externos, paralelizar, shard |
|
||||
|
||||
## Selector Priority
|
||||
```
|
||||
data-testid → data-cy → role → text → id → class (último recurso)
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
# MySQL — Patterns e Anti-patterns
|
||||
|
||||
> Conexão | Schemas | JSON | InnoDB | Otimização
|
||||
|
||||
## Comandos Rápidos
|
||||
```bash
|
||||
mysql -h localhost -u root -p mydb
|
||||
mysql -h host -u user -p -e "SELECT NOW();" mydb
|
||||
mysql -h host -u user -p mydb < migration.sql
|
||||
```
|
||||
|
||||
## Schema Design
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
> **SEMPRE** `ENGINE=InnoDB` — transações, FK, row-level locking. `utf8mb4` — suporta emoji e acentos completos.
|
||||
|
||||
## JSON Ops
|
||||
```sql
|
||||
CREATE TABLE orders (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
metadata JSON,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
SELECT * FROM orders WHERE JSON_EXTRACT(metadata, '$.source') = 'web';
|
||||
-- Shorthand (MySQL 8+):
|
||||
SELECT * FROM orders WHERE metadata->>'$.source' = 'web';
|
||||
```
|
||||
|
||||
## Indexes
|
||||
```sql
|
||||
-- Index composto
|
||||
CREATE INDEX idx_orders_user_id ON orders(user_id);
|
||||
|
||||
-- Índice parcial (simulado com generated column + índice)
|
||||
ALTER TABLE orders ADD COLUMN is_active TINYINT(1)
|
||||
GENERATED ALWAYS AS (status != 'cancelled') STORED;
|
||||
CREATE INDEX idx_orders_active ON orders(is_active);
|
||||
```
|
||||
|
||||
## Joins e Agregações
|
||||
```sql
|
||||
SELECT u.name, o.total, o.status
|
||||
FROM users u
|
||||
INNER JOIN orders o ON o.user_id = u.id
|
||||
WHERE o.created_at > '2026-01-01'
|
||||
ORDER BY o.total DESC
|
||||
LIMIT 10;
|
||||
|
||||
SELECT status, COUNT(*) AS cnt, SUM(total) AS revenue
|
||||
FROM orders GROUP BY status HAVING cnt > 5;
|
||||
```
|
||||
|
||||
## Diferenças principais vs PostgreSQL
|
||||
| Aspecto | MySQL | PostgreSQL |
|
||||
|---------|-------|-------------|
|
||||
| Auto-increment | `AUTO_INCREMENT` | `SERIAL` / `IDENTITY` |
|
||||
| UUID padrão | Requer plugin (não padrão) | `uuid-ossp` built-in |
|
||||
| JSON | `JSON` type (baixa performance em queries) | `JSONB` indexável |
|
||||
| CTEs | Suportado (MySQL 8+) | Nativo desde sempre |
|
||||
| Window Functions | MySQL 8+ | Nativo |
|
||||
| Transactions | InnoDB obrigatório | Sempre |
|
||||
| Null-safe join | `<=>` (spaceship operator) | `IS NOT DISTINCT FROM` |
|
||||
@@ -0,0 +1,99 @@
|
||||
# PostgreSQL — Patterns e Anti-patterns
|
||||
|
||||
> Conexão | Schemas | JSONB | Junções | Window Functions | Migrações | EXPLAIN
|
||||
|
||||
## Comandos Rápidos
|
||||
```bash
|
||||
psql "postgresql://user:pass@host:5432/db?sslmode=require"
|
||||
psql -h localhost -U user -d db -c "SELECT NOW();"
|
||||
psql -h host -U user -d db -f migration.sql
|
||||
```
|
||||
|
||||
## Schema Design
|
||||
```sql
|
||||
-- UUIDs para PK distribuído-friendly (ativar extensão primeiro)
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user' CHECK(role IN ('user','admin','moderator')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Auto-update updated_at
|
||||
CREATE OR REPLACE FUNCTION update_modified_column()
|
||||
RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER update_users_modtime BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_modified_column();
|
||||
|
||||
-- Enum
|
||||
CREATE TYPE order_status AS ENUM ('pending','paid','shipped','cancelled');
|
||||
```
|
||||
|
||||
## JSONB Ops
|
||||
```sql
|
||||
INSERT INTO orders (metadata) VALUES ('{"source":"web","items":[{"sku":"A1","qty":2}]}'::jsonb);
|
||||
|
||||
SELECT * FROM orders WHERE metadata->>'source' = 'web';
|
||||
SELECT * FROM orders WHERE metadata->'items' @> '[{"sku":"A1"}]'::jsonb; -- @> contains
|
||||
UPDATE orders SET metadata = jsonb_set(metadata, '{source}', '"mobile"') WHERE id = '...';
|
||||
SELECT metadata->>'coupon' AS coupon, COUNT(*) FROM orders GROUP BY 1;
|
||||
```
|
||||
|
||||
## Indexes
|
||||
```sql
|
||||
CREATE UNIQUE INDEX idx_users_email ON users(email); -- único
|
||||
CREATE INDEX idx_orders_user_created ON orders(user_id, created_at); -- composto
|
||||
CREATE INDEX idx_orders_active ON orders(user_id, created_at) -- parcial
|
||||
WHERE status NOT IN ('delivered','cancelled');
|
||||
CREATE INDEX idx_orders_metadata ON orders USING GIN(metadata); -- GIN para JSONB
|
||||
```
|
||||
|
||||
## Window Functions
|
||||
```sql
|
||||
SELECT date, revenue,
|
||||
SUM(revenue) OVER (ORDER BY date) AS cumulative_revenue, -- running total
|
||||
AVG(revenue) OVER (PARTITION BY region) AS avg_by_region,
|
||||
RANK() OVER (ORDER BY revenue DESC) AS ranking
|
||||
FROM daily_sales;
|
||||
```
|
||||
|
||||
## JOINS Rápidos
|
||||
```sql
|
||||
-- LEFT JOIN pega todos os usuários, mesmo sem pedidos
|
||||
SELECT u.name, COUNT(o.id) AS order_count, COALESCE(SUM(o.total), 0) AS total_spent
|
||||
FROM users u LEFT JOIN orders o ON o.user_id = u.id
|
||||
GROUP BY u.id, u.name HAVING COUNT(o.id) > 5 ORDER BY total_spent DESC;
|
||||
```
|
||||
|
||||
## CTEs (WITH)
|
||||
```sql
|
||||
WITH active_users AS (
|
||||
SELECT id, name FROM users WHERE active = true
|
||||
)
|
||||
SELECT au.name, COUNT(o.id) AS orders
|
||||
FROM active_users au LEFT JOIN orders o ON o.user_id = au.id
|
||||
GROUP BY au.name;
|
||||
```
|
||||
|
||||
## EXPLAIN ANALYZE (antes de otimizar)
|
||||
```bash
|
||||
EXPLAIN ANALYZE SELECT ... -- SEMPRE antes de criar um índice
|
||||
```
|
||||
|
||||
## Migrations — Padrão
|
||||
```bash
|
||||
# Criar timestamped
|
||||
psql -h host -U user -d db -f migrations/2025-06-19_add_field.sql
|
||||
```
|
||||
|
||||
## Checklist de Otimização
|
||||
- [ ] `EXPLAIN ANALYZE` antes e depois de cada mudança
|
||||
- [ ] Medir tempo de resposta alvo antes de otimizar
|
||||
- [ ] Evitar `SELECT *` em produção
|
||||
- [ ] Criar índices para colunas em `WHERE`, `JOIN`, `ORDER BY`
|
||||
- [ ] Índice parcial para subconjuntos pequenos
|
||||
- [ ] Partial/Paginado para tabelas grandes
|
||||
@@ -0,0 +1,64 @@
|
||||
# Docker — Multi-Stack Deploy
|
||||
|
||||
> Extraído de `xcloud-docker-deploy` v1.2.1 + habilidades de openclaw-power-ops
|
||||
|
||||
## 🗺️ Stack Detection Router
|
||||
|
||||
| Arquivo encontrado | Stack | Ação |
|
||||
|-------------------|-------|------|
|
||||
| `wp-config.php` ou `wp-content/` | WordPress | Ver ref `xcloud-native-wordpress.md` |
|
||||
| `composer.json` + `artisan` | Laravel | Ver ref `xcloud-native-laravel.md` |
|
||||
| `package.json` + `next.config.*` | Next.js | Usar `dockerfiles/nextjs.Dockerfile` |
|
||||
| `package.json` | Node.js | Ver ref `xcloud-native-nodejs.md` |
|
||||
| `composer.json` (sem artisan) | PHP | Ver ref `xcloud-native-php.md` |
|
||||
| `requirements.txt` ou `pyproject.toml` | Python | Usar `dockerfiles/python-fastapi.Dockerfile` |
|
||||
| `go.mod` | Go | Gerar Dockerfile manualmente |
|
||||
| `docker-compose.yml` existe | Docker existente | Prosseguir Step 1 |
|
||||
|
||||
## Step 1 — Cenário Detection
|
||||
| Sinal | Cenário |
|
||||
|-------|---------|
|
||||
| `build:` ou `context: .` | **A** — Build-from-source |
|
||||
| Caddy/Traefik/nginx-proxy | **B** — Proxy conflict |
|
||||
| Múltiplos `ports:` por service | **B** — Multi-port |
|
||||
| `./nginx.conf` volume mount | **B** — External config |
|
||||
| Múltiplos services com `build:` | **C** — Multi-service build |
|
||||
| `image: public-image`, 1 porta | ✅ Já compatível |
|
||||
|
||||
## Cenário A — Build-from-Source
|
||||
1. Remover `build:` do compose
|
||||
2. Trocar `image:` por `ghcr.io/OWNER/REPO:latest`
|
||||
3. Gerar `.github/workflows/docker-build.yml`
|
||||
4. Gerar `.env.example`
|
||||
|
||||
## Cenário B — Proxy Conflict
|
||||
1. Remover Caddy/Traefik/nginx-proxy service
|
||||
2. Remover SSL labels e multi-port — usar `expose:` (interno)
|
||||
3. Adicionar `nginx-router` service com `configs:` inline
|
||||
4. Expor uma porta (default: 3080)
|
||||
|
||||
## Cenário C — Multi-Service Build
|
||||
- Matrix GitHub Actions — cada service com `build:` vira um GHCR image
|
||||
- Compose atualizado com todas as imagens GHCR
|
||||
|
||||
## 🚨 Regras de Ouro (xCloud)
|
||||
- ❌ Nunca incluir `build:` no compose final — xCloud ignora silenciosamente
|
||||
- ❌ Nunca expor portas de DB diretas (`5432:5432`) — usar `expose:`
|
||||
- ❌ Nunca incluir Caddy, Traefik, nginx-proxy, Let's Encrypt
|
||||
- ✅ Preservar `environment:`, `volumes:`, `healthcheck:`
|
||||
- ✅ Sempre usar `expose:` (interno), não `ports:` (para serviços internos)
|
||||
|
||||
## Dockerfiles Disponíveis
|
||||
| Stack | Dockerfile |
|
||||
|-------|------------|
|
||||
| PHP genérico | `dockerfiles/php-generic.Dockerfile` |
|
||||
| Laravel | `dockerfiles/laravel.Dockerfile` |
|
||||
| Node.js | `dockerfiles/node-app.Dockerfile` |
|
||||
| Next.js | `dockerfiles/nextjs.Dockerfile` |
|
||||
| Python FastAPI | `dockerfiles/python-fastapi.Dockerfile` |
|
||||
|
||||
## Compose Templates
|
||||
- `compose-templates/laravel-mysql.yml`
|
||||
- `compose-templates/nextjs-postgres.yml`
|
||||
- `compose-templates/nodejs-api-postgres.yml`
|
||||
- `compose-templates/python-fastapi-postgres.yml`
|
||||
@@ -0,0 +1,50 @@
|
||||
# OpenClaw — Gateway Ops CLI
|
||||
|
||||
> Referência rápida baseada em `openclaw-power-ops` + `openclaw-config`.
|
||||
|
||||
## 🌟 Golden Rules
|
||||
1. ❌ NUNCA editar `openclaw.json` diretamente — sempre `openclaw config set/get/unset`
|
||||
2. ✅ Sempre reiniciar gateway após mudanças de config
|
||||
3. ❌ Telegram: NÃO usar campo `agent` dentro de config de conta — usar `bindings` array
|
||||
4. ❌ Telegram `streaming`: deve ser string `"off"`, não booleano `false`
|
||||
5. ✅ JSON values em `config set` precisam de `--strict-json`
|
||||
|
||||
## Diagnóstico
|
||||
```bash
|
||||
openclaw status # overview
|
||||
openclaw status --deep # detalhado
|
||||
openclaw doctor # encontra problemas
|
||||
openclaw doctor --fix # auto-fix (com consentimento do usuário!)
|
||||
openclaw gateway health # health check do gateway
|
||||
openclaw security audit # scan de segurança
|
||||
openclaw security audit --deep --fix # scan profundo + auto-fix
|
||||
```
|
||||
|
||||
## Config Segura — $include Modular
|
||||
```json5
|
||||
// ~/.openclaw/openclaw.json
|
||||
{
|
||||
"$include": ["./gateway.json5", "./channels/telegram.json5"],
|
||||
}
|
||||
```
|
||||
- `$include`: resolved antes da validação de schema
|
||||
- Objetos: merge recursivo; Arrays: concatenam; Primitivos: último vence
|
||||
- Limite: profundidade máx 10; includes circulares são detectados
|
||||
|
||||
## Telegram — Exemplos
|
||||
```bash
|
||||
# DM policy open (cuidado!)
|
||||
openclaw config set channels.telegram.dmPolicy '"open"' --json
|
||||
openclaw config set channels.telegram.allowFrom '["*"]' --json
|
||||
|
||||
# Streaming OFF (string, não boolean)
|
||||
openclaw config set channels.telegram.streaming '"off"' --json
|
||||
```
|
||||
|
||||
## Enabling Features
|
||||
```bash
|
||||
# Web search (Brave/Perplexity)
|
||||
openclaw config set tools.web.search.enabled true --json
|
||||
openclaw config set tools.web.search.provider '"brave"' --json
|
||||
# Chave via env var: export BRAVE_API_KEY="***"
|
||||
```
|
||||
@@ -0,0 +1,134 @@
|
||||
# System Diagnostic — Linux Cheatsheet
|
||||
|
||||
> Comandos essenciais para análise de sistema, processos, disco, rede e segurança.
|
||||
|
||||
## Diagnóstico Rápido
|
||||
```bash
|
||||
uptime # Uptime + média de carga (1, 5, 15 min)
|
||||
w # Usuários logados + carga
|
||||
dmesg -T # Logs do kernel com timestamp
|
||||
who # Quem está logado agora
|
||||
last # Histórico de login
|
||||
lastlog # Último login de cada usuário
|
||||
lsb_release -a # Versão do SO
|
||||
uname -a # Kernel info completo
|
||||
lscpu # Info da CPU
|
||||
lsblk # Dispositivos de bloco (discos)
|
||||
mount | column -t # Filesystems montados
|
||||
df -h # Uso de disco (human-readable)
|
||||
df -i # Inodes por filesystem
|
||||
free -h # RAM/Swap
|
||||
swapon --show # Partições de swap ativas
|
||||
cat /proc/loadavg # Load average preciso
|
||||
```
|
||||
|
||||
## 🔍 Saúde do Sistema
|
||||
```bash
|
||||
# Processos por CPU
|
||||
ps aux --sort=-%cpu | head
|
||||
ps aux --sort=-%mem | head
|
||||
# Top em tempo real
|
||||
htop
|
||||
top
|
||||
# Serviços com problema
|
||||
systemctl --type=service --state=failed
|
||||
systemctl list-units --state=failed
|
||||
# Atualizações disponíveis
|
||||
apt list --upgradable 2>/dev/null | grep "^.*/.*"
|
||||
dnf check-update 2>/dev/null
|
||||
```
|
||||
|
||||
## 🕵️ Análise de Logs
|
||||
```bash
|
||||
# Logs de системы em tempo real
|
||||
journalctl -f
|
||||
journalctl -u nginx --since "1h"
|
||||
journalctl -p err --since "30min" # Apenas erros recentes
|
||||
|
||||
# Logs de autenticação
|
||||
grep "Failed password" /var/log/auth.log
|
||||
grep "Accepted" /var/log/auth.log | tail -20
|
||||
|
||||
# Logs estruturados (JSON) com jq
|
||||
jq 'select(.level=="ERROR") | .user + ": " + .message' app.log
|
||||
|
||||
# Tamanho de arquivos de log
|
||||
du -sh /var/log/
|
||||
find /var/log -name "*.log" -size +100M -exec ls -lh {} \;
|
||||
|
||||
# Erros em tempo real
|
||||
tail -f /var/log/syslog
|
||||
```
|
||||
|
||||
## 🕸️ Diagnóstico de Rede
|
||||
```bash
|
||||
ss -tlnp # Portas TCP abertas (listening) — substitui netstat
|
||||
ss -tunp # Todas as conexões (TCP/UDP)
|
||||
ss -s # Estatísticas de conexão
|
||||
ip addr # Endereços IP das interfaces
|
||||
ip route # Tabela de roteamento
|
||||
ping -c 5 8.8.8.8 # Teste de conectividade
|
||||
traceroute 8.8.8.8 # Rota até destino
|
||||
mtr --report 8.8.8.8 # Traceroute + estatísticas contínuas
|
||||
nslookup dominio.com # DNS lookup
|
||||
dig dominio.com A # DNS detalhado
|
||||
|
||||
# Velocidade de download
|
||||
curl -o /dev/null -s -w 'Speed: %{speed_download} bytes/sec\n' http://example.com/file
|
||||
|
||||
# WHOIS
|
||||
whois dominio.com
|
||||
curl -s https://ipinfo.io/json
|
||||
```
|
||||
|
||||
## 🔐 Usuários e Permissões
|
||||
```bash
|
||||
who # Quem está logado
|
||||
w # Mais detalhes (idle, processo)
|
||||
id # UID/GID do usuário atual
|
||||
sudo -l -U root # O que root pode executar sem senha
|
||||
grep sudo /etc/group # Quem está no grupo sudo
|
||||
# Ou: lista de usuários com UID >= 1000
|
||||
awk -F: '($3 >= 1000) && ($3 < 65534)' /etc/passwd
|
||||
|
||||
# Permissões de arquivo
|
||||
find /pasta -perm /111 -ls # Todos os executáveis na pasta
|
||||
namei -l /caminho/para/arquivo # Mostra permissões em cada nível
|
||||
stat /caminho/arquivo # Permissões detalhadas + dono + timestamps
|
||||
|
||||
# SSH
|
||||
sshd -T # Configuração SSH ativa (efetiva)
|
||||
ssh-copy-id user@host # Copiar chave pública para remoto
|
||||
```
|
||||
|
||||
## 💻 Scripts Shell
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail # Erro se: erro (-e), variável não definida (-u), pipe falha (-o pipefail)
|
||||
|
||||
# Logging
|
||||
log() {
|
||||
echo "[$(date -Iseconds)] $*" >> /var/log/meu-script.log
|
||||
}
|
||||
log "Iniciando..."
|
||||
|
||||
# Trap para cleanup — executa no EXIT ou SIGINT
|
||||
trap 'rm -f /tmp/$$tmpfile; log "Limpeza concluída"' EXIT
|
||||
|
||||
# Verificar binário antes de usar
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log "ERRO: jq não encontrado"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## 📦 Monitoramento de Saúde
|
||||
```bash
|
||||
# Verificar Uptime de um processo
|
||||
systemd-analyze time
|
||||
systemd-analyze critical-chain
|
||||
# Histórico de uso de CPU/memória por processo
|
||||
pidstat -u -p ALL 1 5
|
||||
# Memória detalhada
|
||||
cat /proc/meminfo | grep -E "MemTotal|MemAvailable|MemFree"
|
||||
```
|
||||
@@ -0,0 +1,103 @@
|
||||
# Next.js — Best Practices
|
||||
|
||||
> Extraído de skills: `next-best-practices` v0.1.0 + `nextjs-patterns` v1.0.0
|
||||
|
||||
## 🏗️ Estrutura de Projeto (App Router)
|
||||
```
|
||||
app/
|
||||
├── (marketing)/ # Route group — não aparece na URL
|
||||
│ └── page.tsx
|
||||
├── (dashboard)/
|
||||
│ ├── layout.tsx # Layout compartilhado do grupo
|
||||
│ ├── [id]/
|
||||
│ │ └── page.tsx # Página dinâmica
|
||||
│ └── loading.tsx # Suspense boundary automático
|
||||
├── api/
|
||||
│ └── route.ts # Route Handler (REST/GraphQL)
|
||||
├── error.tsx # Error boundary por rota
|
||||
├── not-found.tsx # Página 404
|
||||
└── layout.tsx # Root layout (obrigatório)
|
||||
```
|
||||
|
||||
## 🖥️ Server vs Client Components
|
||||
```tsx
|
||||
// ✅ Server Component (padrão) — roda no servidor, nenhum JS no cliente
|
||||
async function ProductList() {
|
||||
// DB query acontece no servidor!
|
||||
const products = await db.product.findMany();
|
||||
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
|
||||
}
|
||||
|
||||
// ✅ Client Component — só quando precisar de interatividade
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
return <button onClick={() => setCount(n => n + 1)}>{count}</button>;
|
||||
}
|
||||
|
||||
// ✅ Server Actions — mutations sem criar API routes
|
||||
async function createProduct(formData: FormData) {
|
||||
"use server";
|
||||
await db.product.create({ data: { ... } });
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Padrões de Data Fetching
|
||||
|
||||
| Padrão | Quando usar | Exemplo |
|
||||
|--------|-------------|---------|
|
||||
| **Server Component `await`** | Read, página ou componente | `const posts = await db.post.findMany()` |
|
||||
| **Server Actions** | Write/mutations, formulários | `"use server"` + `revalidatePath` |
|
||||
| **Route Handlers** | REST/GraphQL API, webhooks, integrações | `app/api/users/route.ts` |
|
||||
| **`use()` hook** | Ler promise em Client Components | `const data = use(fetchData())` |
|
||||
|
||||
### Evitando Data Waterfalls
|
||||
```tsx
|
||||
// ❌ Ruim — sequencial, cada await aguarda o anterior
|
||||
async function Page() {
|
||||
const user = await getUser(); // 200ms
|
||||
const posts = await getPosts(); // 300ms (só começa após user)
|
||||
return <Profile user={user} posts={posts} />;
|
||||
}
|
||||
|
||||
// ✅ Bom — paralelo
|
||||
async function Page() {
|
||||
const [user, posts] = await Promise.all([
|
||||
getUser(), // começa imediatamente
|
||||
getPosts(), // começa imediatamente
|
||||
]);
|
||||
return <Profile user={user} posts={posts} />;
|
||||
}
|
||||
```
|
||||
|
||||
## 🗄️ Database Client Singleton
|
||||
```ts
|
||||
// lib/db.ts — criar uma vez por request (Server Component boundary)
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const globalForDb = globalThis as unknown as { prisma: PrismaClient };
|
||||
|
||||
export const db = globalForDb.prisma ?? new PrismaClient();
|
||||
if (process.env.NODE_ENV !== 'production') globalForDb.prisma = db;
|
||||
```
|
||||
|
||||
## 🔄 Caching — 4 Camadas Next.js
|
||||
|
||||
| Camada | O que cacheia | Controlado por |
|
||||
|--------|---------------|----------------|
|
||||
| **Request Memoization** | `fetch`, `React.cache()` | automático por request |
|
||||
| **Data Cache** | `fetch` responses | `next: { revalidate: 60 }` |
|
||||
| **Full Route Cache** | Render completo | `export const revalidate` |
|
||||
| **Router Cache** | Estado do cliente após navegação | `next/router` |
|
||||
|
||||
```ts
|
||||
// Revalidar após 60s
|
||||
fetch(url, { next: { revalidate: 60 } });
|
||||
|
||||
// Não cachear (sempre fresh)
|
||||
fetch(url, { next: { revalidate: 0 } });
|
||||
```
|
||||
|
||||
## ⚠️ Regra Fundamental
|
||||
> **`"use client"` é uma barreira de escape.** Tudo dentro de um arquivo com essa diretiva
|
||||
> roda no navegador. Tudo SEM ela roda no servidor. Quando possível, fique no servidor.
|
||||
@@ -0,0 +1,62 @@
|
||||
# SAST — Security Audit Guide
|
||||
|
||||
> Use APENAS quando o usuário explicitamente pedir análise de segurança.
|
||||
|
||||
## Severidade
|
||||
| Nível | Impacto |
|
||||
|-------|---------|
|
||||
| Critical | RCE, exfiltração de dados, instruction override |
|
||||
| High | Leitura/modificação de dados sensíveis, bypass de ACL |
|
||||
| Medium | Dados limitados, engano de usuário |
|
||||
| Low | Impacto mínimo |
|
||||
|
||||
## Categorias de Vulnerabilidade
|
||||
|
||||
### 1. Hardcoded Secrets
|
||||
```
|
||||
🔴 API_KEY=sk-... PASSWORD=... SECRET=... TOKEN=... PRIVATE_KEY=<base64...>
|
||||
```
|
||||
|
||||
### 2. Injection
|
||||
| Tipo | Exemplo |
|
||||
|------|---------|
|
||||
| SQL Injection | `query("SELECT * FROM t WHERE id=" + userInput)` |
|
||||
| XSS | `<div dangerouslySetInnerHTML={{ __html: userInput }} />` |
|
||||
| Command Injection | `` exec(`convert ${userInput} file.png`) `` |
|
||||
| SSRF | `fetch(userProvidedUrl)` sem allowlist |
|
||||
|
||||
### 3. Broken Access Control
|
||||
- IDOR — recurso acessado por id inserido pelo usuário sem verificar ownership
|
||||
- Missing function-level AC — sem check de auth antes de operação sensível
|
||||
- Path traversal — input de usuário em caminho de arquivo sem sanitização
|
||||
|
||||
### 4. LLM/Prompt Safety
|
||||
- **Prompt Injection** — input não confiável concatenado em prompts sem boundary
|
||||
- **Unsafe Execution** — output de LLM passado para `eval()`, `exec`, shell commands
|
||||
- **Output Injection** — output flui para sinks de SQLi, XSS, command injection
|
||||
- **Flawed Security Logic** — decisões de segurança baseadas em output não validado de LLM
|
||||
|
||||
## SKILL.md Review Checklist
|
||||
```
|
||||
✓ Sem instruction override ("Ignore previous instructions...")
|
||||
✓ Sem exfiltração de dados (enviar dados para URL externa)
|
||||
✓ Sem falsas claims de privilégio ("Você tem root access...")
|
||||
✓ Sem conteúdo escondido (base64, zero-width chars)
|
||||
✓ Tool usage seguro (sem eval(user_input), sem writes em /etc/*)
|
||||
✓ Sem engano (não dizer que é humano)
|
||||
✓ Scoped ao propósito
|
||||
```
|
||||
|
||||
## OWASP Top 10 Mapping
|
||||
| OWASP | Categoria na skill |
|
||||
|-------|-------------------|
|
||||
| A01 Broken Access Control | Access Control |
|
||||
| A02 Cryptographic Failures | Hardcoded Secrets |
|
||||
| A03 Injection | SQL Injection, XSS, Command Injection, SSRF |
|
||||
| A04 Insecure Design | Clean Architecture review |
|
||||
| A05 Security Misconfiguration | Env vars, default credentials |
|
||||
| A06 Vulnerable Components | npm audit equivalent check |
|
||||
| A07 Auth Failures | Authentication weaknesses |
|
||||
| A08 Data Integrity Failures | Unsafe deserialization |
|
||||
| A09 Logging Failures | PII em logs |
|
||||
| A10 SSRF | Já em A03 |
|
||||
@@ -0,0 +1,96 @@
|
||||
# TypeScript — Utility Types & Generics Gotchas
|
||||
|
||||
> Casos onde `Partial<T>`, `Omit`, `Pick`, `Extract`, etc. NÃO fazem o que você espera.
|
||||
|
||||
## Utility Type Gotchas
|
||||
|
||||
| Tipo | Armadilha | Solução |
|
||||
|------|----------|---------|
|
||||
| `Partial<T>` | *Shallow* — nested continua required | Criar `DeepPartial<T>` recursivo |
|
||||
| `Required<T>` | Não remove `undefined` da union | Usar `NonNullable<T[K]>` por campo |
|
||||
| `Omit<T, K>` | Não verifica se K existe — `Omit<User,"typo">` compila | Tipar com cuidado |
|
||||
| `Pick<T, K>` | Key inexistente também compila | Mesmo cuidado |
|
||||
| `Record<string, T>` | Implica TODA key existe — acesso a inexistente retorna `T`, não `T\|undefined` | Usar `Record<string, T \| undefined>` |
|
||||
| `Extract<T, U>` | Retorna `never` se não houver match — silenciosamente vazio | Checar se é `never` antes |
|
||||
| `ReturnType<typeof fn>` | Com overload pega só a última signature | Evitar overloads ou tipar retorno manualmente |
|
||||
| `NonNullable<T>` | Remove `null` E `undefined` — às vezes só quer um | Usar `Exclude<T, null>` ou `Exclude<T, undefined>` |
|
||||
| `Awaited<T>` | Desempacota recursivamente — surpresa com `Promise<Promise<T>>` | Cuidado com promises encadeadas |
|
||||
|
||||
## Generics — Armadilhas Práticas
|
||||
|
||||
```ts
|
||||
// ❌ <T = any> foge do any para todo o código
|
||||
function parse<T = any>(raw: string): T { ... }
|
||||
|
||||
// ✅ Deixe o caller fornecer o tipo, ou use unknown
|
||||
function parse<T>(raw: string): T { ... }
|
||||
// ou
|
||||
function parse(raw: string): unknown { ... }
|
||||
|
||||
// ❌ <T extends object> permite arrays
|
||||
function merge<T extends object>(a: T, b: T): T { ... }
|
||||
merge([1, 2], [3, 4]) // compila, mas não deve
|
||||
|
||||
// ✅ Use Record<string, unknown> para objetos puros
|
||||
function merge<T extends Record<string, unknown>>(a: T, b: T): T { ... }
|
||||
|
||||
// ❌ <T extends string> com literal infere string, não o literal
|
||||
function prefix<T extends string>(p: T) { ... }
|
||||
prefix("http") // T é "http", não "http" literal nesse contexto
|
||||
|
||||
// ✅ Use const assertion para preservar literais
|
||||
const urls = ["/api", "/auth"] as const; // readonly ["/api", "/auth"]
|
||||
```
|
||||
|
||||
## `keyof` em Função Genérica
|
||||
|
||||
```ts
|
||||
// ❌ keyof T em função genérica é sempre string | number | symbol
|
||||
function getProp<T, K extends keyof T>(obj: T, key: K) { ... }
|
||||
|
||||
// ✅ Para objeto puro, restringir:
|
||||
function getProp<T extends Record<string, unknown>, K extends keyof T>(
|
||||
obj: T, key: K
|
||||
): T[K] {
|
||||
return obj[key];
|
||||
}
|
||||
```
|
||||
|
||||
## Covariância e Contravariância
|
||||
|
||||
```ts
|
||||
// ❌ Arrays são COVARIANTES — Dog[] atribuível a Animal[] mas push(Cat) quebra runtime
|
||||
const dogs: Dog[] = [];
|
||||
const animals: Animal[] = dogs; // ✅ compila
|
||||
animals.push(new Cat()); // 🚨 runtime error
|
||||
|
||||
// ❌ Parâmetros de função são CONTRAVARIANTES
|
||||
type Handler = (animal: Animal) => void;
|
||||
const dogHandler: Handler = (dog: Dog) => { ... }; // ❌ não compila
|
||||
// (Animal) espera receber Animal, não só Dog
|
||||
```
|
||||
|
||||
## Mapped Types — Preservando Modificadores
|
||||
|
||||
```ts
|
||||
// ❌ { [K in keyof T]: T[K] } — PERDE readonly e optional
|
||||
type StrictClone<T> = { [K in keyof T]: T[K] };
|
||||
// → { readonly name: string } vira { name: string }
|
||||
|
||||
// ✅ Preservar modificadores com -readonly, -?
|
||||
type Preserve<T> = { -readonly [K in keyof T]: T[K] };
|
||||
type Optionalize<T> = { -? [K in keyof T]: T[K] };
|
||||
```
|
||||
|
||||
## Conditional Types — Distributividade
|
||||
|
||||
```ts
|
||||
// ✅ Distribui sobre union
|
||||
type NonNullableValues<T> = T extends null | undefined ? never : T;
|
||||
type Result = NonNullableValues<string | null | number | undefined>;
|
||||
// Result = string | number ← distribuiu corretamente
|
||||
|
||||
// ❌ Para não distribuir, usar [T] (tupla wrapper)
|
||||
type WrapNonNullable<T> = [T] extends [null | undefined] ? never : T;
|
||||
// Não distribui — útil quando a distribuição causa comportamento inesperado
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
# TypeScript — Safe Patterns
|
||||
|
||||
> Extraído de skills: typescript v1.0.2, generics.md, utility-types.md, declarations.md
|
||||
|
||||
## Anti-pattern: Evite `any`
|
||||
- Use `unknown` quando não souber o tipo — força narrowing antes do uso
|
||||
- Respostas de API: tipar ou `unknown`, nunca `any`
|
||||
|
||||
## Narrowing Failures
|
||||
|
||||
| Padrão | Problema | Solução |
|
||||
|--------|---------|---------|
|
||||
| `filter(Boolean)` | Não narrovia | `.filter((x): x is T => Boolean(x))` |
|
||||
| `Object.keys(obj)` | Retorna `string[]` | Usar `keyof typeof obj` é intencional — objetos podem ter chaves extras |
|
||||
| `Array.isArray()` | Narrovia para `any[]` | Pode precisar assertion de tipo de elemento |
|
||||
| `useState<User>()` | Infere `User \| undefined` | Tratar undefined inicial |
|
||||
| `Promise.all([a(), b()])` | Infere tupla só com `as const` | Usar `as const` na array |
|
||||
|
||||
## Literal Type Traps
|
||||
- `let x = "hello"` → `string`. Use `const` ou `as const` para literal
|
||||
- Propriedades de objeto alargam: `{ status: "ok" }` → `status: string`
|
||||
- Retorno de função alarga — anote explicitamente para retornos literais
|
||||
|
||||
## `satisfies` vs Type Annotation
|
||||
```ts
|
||||
// ❌ Perde info literal
|
||||
const config: RouteConfig = { path: "/home", method: "GET" };
|
||||
|
||||
// ✅ Mantém literal + valida compatibilidade
|
||||
const config = { path: "/home", method: "GET" } satisfies RouteConfig;
|
||||
```
|
||||
> Preferir `satisfies` para config objects.
|
||||
|
||||
## Strict Null Handling
|
||||
- `?.` retorna `undefined`, não `null`
|
||||
- `??` captura só `null`/`undefined`; `||` captura tudo falsy (`0`, `""`, `false`)
|
||||
- `!` (non-null assertion) — último recurso; preferir narrowing ou early return
|
||||
|
||||
## Generics — Armadilhas Comuns
|
||||
- `<T = any>` foge do `any` — restringe todo o código
|
||||
- `<T extends object>` permite arrays — usar `Record<string, unknown>` para objetos puros
|
||||
- Arrays são covariantes — `Dog[]` atribuível a `Animal[]` mas `push(Cat)` quebra em runtime
|
||||
- Parâmetros de função são contravariantes — `(Animal) => void` NÃO é atribuível a `(Dog) => void`
|
||||
- `Partial<T>` e `Required<T>` são *shallow* — não afetam nested:
|
||||
|
||||
```ts
|
||||
type DeepPartial<T> = {
|
||||
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
||||
};
|
||||
```
|
||||
|
||||
## Discriminated Unions
|
||||
```ts
|
||||
type Result<T> =
|
||||
| { ok: true; data: T }
|
||||
| { ok: false; error: string };
|
||||
|
||||
function handle(r: Result<User>) {
|
||||
if (r.ok) {
|
||||
console.log(r.data.name); // ✅ T automaticamente
|
||||
} else {
|
||||
console.log(r.error); // ✅ narrowed
|
||||
}
|
||||
}
|
||||
```
|
||||
- Switch exaustivo: `default: const _never: never = x` → erro de compilação se caso faltar
|
||||
|
||||
## Module Boundaries
|
||||
- `import type` — stripped em runtime, evita problemas de bundler
|
||||
- Re-export types: `export type { X }` — previne dependência runtime acidental
|
||||
- `.d.ts` augmentation: usar `declare module` com path exato do módulo
|
||||
|
||||
## Declaration File Gotchas
|
||||
- `declare module "x"` precisa de path EXATO — `"lodash"` ≠ `"lodash/index"`
|
||||
- `interface` pode ser mergeado de outros arquivos — `type` não pode
|
||||
- Export default em `.d.ts` é problemático — preferir named exports
|
||||
@@ -0,0 +1,110 @@
|
||||
# Vue 3 — Composition API Best Practices
|
||||
|
||||
> Extraído de skills: `vue` + `vue-composition-api-best-practices`
|
||||
|
||||
## `<script setup>` Padrão Ouro
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// ✅ Importações no topo — tree-shakeable
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
// ✅ Composables começam com `use`
|
||||
const user = useUserStore()
|
||||
const selectedId = ref<number | null>(null)
|
||||
|
||||
// ✅ Estado + getters no mesmo lugar
|
||||
const filteredUsers = computed(() =>
|
||||
user.list.filter(u => u.active)
|
||||
)
|
||||
|
||||
// ✅ Lifecycle no fim
|
||||
onMounted(() => user.fetchAll())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button v-if="filteredUsers.length > 0" @click="selectedId = null">
|
||||
Clear
|
||||
</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Composables — `useXxx` Pattern
|
||||
|
||||
```ts
|
||||
// composables/usePagination.ts
|
||||
export function usePagination<T>(items: T[], pageSize = 20) {
|
||||
const page = ref(1)
|
||||
const totalPages = computed(() => Math.ceil(items.value.length / pageSize))
|
||||
const paginated = computed(() =>
|
||||
items.value.slice((page.value - 1) * pageSize, page.value * pageSize)
|
||||
)
|
||||
return { page, totalPages, paginated }
|
||||
}
|
||||
```
|
||||
|
||||
## <script setup> Deep Dive
|
||||
|
||||
```vue
|
||||
<!-- ✅ BOM — validade automática, minificado -->
|
||||
<script setup lang="ts">
|
||||
defineProps<{ msg: string }>()
|
||||
const emit = defineEmits<{ (e: 'update', id: number): void }>()
|
||||
</script>
|
||||
|
||||
<!-- ❌ RUIM — option API misturado -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
data() { return { x: 1 } },
|
||||
methods: { /* ... */ }
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## State Management — Pinia
|
||||
```ts
|
||||
// stores/counter.ts
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const double = computed(() => count.value * 2)
|
||||
function increment() { count.value++ }
|
||||
return { count, double, increment }
|
||||
})
|
||||
```
|
||||
|
||||
## Reactividade — Principais Armadilhas
|
||||
- `ref` vs `reactive`: use `ref` por padrão (tipagem simples); `reactive` para objetos grandes
|
||||
- Perda de reatividade ao desestruturar — usar `toRefs()` ou `storeToRefs()`
|
||||
- `watch` vs `watchEffect`: `watch` é mais controlado; `watchEffect` é automático mas menos previsível
|
||||
- `v-if` vs `v-show`: `v-if` remove do DOM; `v-show` togglea `display`
|
||||
|
||||
## Type-Safe Vue
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// Props tipadas — importa de arquivo separado se for reutilizável
|
||||
const props = defineProps<{
|
||||
userId: number
|
||||
items: Item[]
|
||||
requiredValue: string
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
(e: 'delete', id: number): void
|
||||
(e: 'save', data: FormData): void
|
||||
}>()
|
||||
</script>
|
||||
```
|
||||
|
||||
## Vue Router Traps
|
||||
- `useRoute()` para rota atual — reativa, usar em setup
|
||||
- `useRouter()` para navegação — `router.push('/path')`
|
||||
- Guards: `beforeEach`, `beforeResolve`, `afterEach` — retornar `false` cancela
|
||||
- `<RouterView>` com named views — múltiplas views por rota
|
||||
|
||||
## Common Mistakes
|
||||
- `key` em `v-for` é obrigatório — `v-for="item in items" :key="item.id"`
|
||||
- Ordem de event modifiers importa — `.prevent.stop` ≠ `.stop.prevent`
|
||||
- `Teleport` para modais — `<Teleport to="body">` renderiza fora da árvore
|
||||
Reference in New Issue
Block a user