@maestro-ai/cli 1.0.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/README.md +59 -0
- package/content/guides/Cat/303/241logo de Stacks para Cloud Moderna.md" +119 -0
- package/content/guides/Cat/303/241logo de Stacks para Hospedagem Compartilhada.md" +147 -0
- package/content/guides/Checklist Mestre de Entrega.md +68 -0
- package/content/guides/Gates de Qualidade.md +209 -0
- package/content/guides/Guia de Adi/303/247/303/243o de Novas Funcionalidades.md" +355 -0
- package/content/guides/Guia de Chaos Engineering.md +267 -0
- package/content/guides/Guia de Debugging com IA.md +135 -0
- package/content/guides/Guia de Estrat/303/251gias de Cache.md" +352 -0
- package/content/guides/Guia de Migrations Zero-Downtime.md +311 -0
- package/content/guides/Guia de Multi-tenancy.md +368 -0
- package/content/guides/Guia de Otimiza/303/247/303/243o de Custos Cloud.md" +195 -0
- package/content/guides/Guia de Refatora/303/247/303/243o de C/303/263digo Legado com IA.md" +162 -0
- package/content/guides/Guia de SLOs e Error Budgets.md +315 -0
- package/content/guides/M/303/251tricas de Efici/303/252ncia do Desenvolvimento com IA.md" +93 -0
- package/content/guides/Rules base.md +90 -0
- package/content/prompts/README.md +203 -0
- package/content/prompts/acessibilidade/analise-acessibilidade.md +257 -0
- package/content/prompts/apis/design-api-rest.md +303 -0
- package/content/prompts/apis/idempotencia.md +254 -0
- package/content/prompts/apis/versionamento.md +313 -0
- package/content/prompts/arquitetura/arquitetura-c4-completo.md +190 -0
- package/content/prompts/arquitetura/clean-architecture.md +151 -0
- package/content/prompts/arquitetura/ddd-bounded-contexts.md +183 -0
- package/content/prompts/arquitetura/ddd-cqrs.md +176 -0
- package/content/prompts/arquitetura/modelo-dominio.md +207 -0
- package/content/prompts/arquitetura/multi-tenancy.md +235 -0
- package/content/prompts/database/migrations-zero-downtime.md +192 -0
- package/content/prompts/database/otimizacao-queries.md +296 -0
- package/content/prompts/desenvolvimento/code-review.md +301 -0
- package/content/prompts/desenvolvimento/gerar-servico.md +271 -0
- package/content/prompts/devops/docker-compose.md +336 -0
- package/content/prompts/devops/feature-flags.md +374 -0
- package/content/prompts/devops/kubernetes-deploy.md +460 -0
- package/content/prompts/devops/pipeline-cicd.md +358 -0
- package/content/prompts/devops/terraform-iac.md +502 -0
- package/content/prompts/escalabilidade/analise-performance.md +240 -0
- package/content/prompts/escalabilidade/analise-performance.txt +94 -0
- package/content/prompts/escalabilidade/caching.md +255 -0
- package/content/prompts/observabilidade/chaos-testing.md +237 -0
- package/content/prompts/observabilidade/estrategia-observabilidade.md +263 -0
- package/content/prompts/observabilidade/estrategia-observabilidade.txt +134 -0
- package/content/prompts/observabilidade/slos.md +215 -0
- package/content/prompts/produto/discovery-inicial.md +203 -0
- package/content/prompts/produto/discovery-inicial.txt +33 -0
- package/content/prompts/requisitos/refinar-requisitos.md +232 -0
- package/content/prompts/requisitos/refinar-requisitos.txt +40 -0
- package/content/prompts/seguranca/analise-seguranca.md +243 -0
- package/content/prompts/seguranca/pentest-checklist.md +333 -0
- package/content/prompts/seguranca/rate-limiting.md +356 -0
- package/content/prompts/seguranca/revisao-lgpd.md +227 -0
- package/content/prompts/seguranca/threat-modeling.md +224 -0
- package/content/prompts/testes/contract-testing.md +340 -0
- package/content/prompts/testes/gerar-testes-unitarios.md +474 -0
- package/content/prompts/testes/testes-e2e.md +460 -0
- package/content/prompts/testes/testes-integracao.md +418 -0
- package/content/prompts/testes/testes-performance.md +458 -0
- package/content/prompts/ux/gerar-ui-stitch.md +151 -0
- package/content/skills/api-patterns/SKILL.md +81 -0
- package/content/skills/api-patterns/api-style.md +42 -0
- package/content/skills/api-patterns/auth.md +24 -0
- package/content/skills/api-patterns/documentation.md +26 -0
- package/content/skills/api-patterns/graphql.md +41 -0
- package/content/skills/api-patterns/rate-limiting.md +31 -0
- package/content/skills/api-patterns/response.md +37 -0
- package/content/skills/api-patterns/rest.md +40 -0
- package/content/skills/api-patterns/scripts/api_validator.py +211 -0
- package/content/skills/api-patterns/security-testing.md +122 -0
- package/content/skills/api-patterns/trpc.md +41 -0
- package/content/skills/api-patterns/versioning.md +22 -0
- package/content/skills/app-builder/SKILL.md +75 -0
- package/content/skills/app-builder/agent-coordination.md +71 -0
- package/content/skills/app-builder/feature-building.md +53 -0
- package/content/skills/app-builder/project-detection.md +34 -0
- package/content/skills/app-builder/scaffolding.md +118 -0
- package/content/skills/app-builder/tech-stack.md +40 -0
- package/content/skills/app-builder/templates/SKILL.md +39 -0
- package/content/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/content/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/content/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/content/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/content/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/content/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/content/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/content/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +82 -0
- package/content/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +100 -0
- package/content/skills/app-builder/templates/nextjs-static/TEMPLATE.md +106 -0
- package/content/skills/app-builder/templates/nuxt-app/TEMPLATE.md +101 -0
- package/content/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/content/skills/app-builder/templates/react-native-app/TEMPLATE.md +93 -0
- package/content/skills/architecture/SKILL.md +55 -0
- package/content/skills/architecture/context-discovery.md +43 -0
- package/content/skills/architecture/examples.md +94 -0
- package/content/skills/architecture/pattern-selection.md +68 -0
- package/content/skills/architecture/patterns-reference.md +50 -0
- package/content/skills/architecture/trade-off-analysis.md +77 -0
- package/content/skills/bash-linux/SKILL.md +199 -0
- package/content/skills/behavioral-modes/SKILL.md +242 -0
- package/content/skills/brainstorming/SKILL.md +163 -0
- package/content/skills/brainstorming/dynamic-questioning.md +350 -0
- package/content/skills/clean-code/SKILL.md +201 -0
- package/content/skills/code-review-checklist/SKILL.md +109 -0
- package/content/skills/database-design/SKILL.md +52 -0
- package/content/skills/database-design/database-selection.md +43 -0
- package/content/skills/database-design/indexing.md +39 -0
- package/content/skills/database-design/migrations.md +48 -0
- package/content/skills/database-design/optimization.md +36 -0
- package/content/skills/database-design/orm-selection.md +30 -0
- package/content/skills/database-design/schema-design.md +56 -0
- package/content/skills/database-design/scripts/schema_validator.py +172 -0
- package/content/skills/deployment-procedures/SKILL.md +241 -0
- package/content/skills/doc.md +177 -0
- package/content/skills/documentation-templates/SKILL.md +194 -0
- package/content/skills/frontend-design/SKILL.md +396 -0
- package/content/skills/frontend-design/animation-guide.md +331 -0
- package/content/skills/frontend-design/color-system.md +311 -0
- package/content/skills/frontend-design/decision-trees.md +418 -0
- package/content/skills/frontend-design/motion-graphics.md +306 -0
- package/content/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/content/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/content/skills/frontend-design/typography-system.md +345 -0
- package/content/skills/frontend-design/ux-psychology.md +541 -0
- package/content/skills/frontend-design/visual-effects.md +383 -0
- package/content/skills/game-development/2d-games/SKILL.md +119 -0
- package/content/skills/game-development/3d-games/SKILL.md +135 -0
- package/content/skills/game-development/SKILL.md +167 -0
- package/content/skills/game-development/game-art/SKILL.md +185 -0
- package/content/skills/game-development/game-audio/SKILL.md +190 -0
- package/content/skills/game-development/game-design/SKILL.md +129 -0
- package/content/skills/game-development/mobile-games/SKILL.md +108 -0
- package/content/skills/game-development/multiplayer/SKILL.md +132 -0
- package/content/skills/game-development/pc-games/SKILL.md +144 -0
- package/content/skills/game-development/vr-ar/SKILL.md +123 -0
- package/content/skills/game-development/web-games/SKILL.md +150 -0
- package/content/skills/geo-fundamentals/SKILL.md +156 -0
- package/content/skills/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/content/skills/i18n-localization/SKILL.md +154 -0
- package/content/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/content/skills/intelligent-routing/SKILL.md +334 -0
- package/content/skills/lint-and-validate/SKILL.md +45 -0
- package/content/skills/lint-and-validate/scripts/lint_runner.py +172 -0
- package/content/skills/lint-and-validate/scripts/type_coverage.py +173 -0
- package/content/skills/mcp-builder/SKILL.md +176 -0
- package/content/skills/mobile-design/SKILL.md +394 -0
- package/content/skills/mobile-design/decision-trees.md +516 -0
- package/content/skills/mobile-design/mobile-backend.md +491 -0
- package/content/skills/mobile-design/mobile-color-system.md +420 -0
- package/content/skills/mobile-design/mobile-debugging.md +122 -0
- package/content/skills/mobile-design/mobile-design-thinking.md +357 -0
- package/content/skills/mobile-design/mobile-navigation.md +458 -0
- package/content/skills/mobile-design/mobile-performance.md +767 -0
- package/content/skills/mobile-design/mobile-testing.md +356 -0
- package/content/skills/mobile-design/mobile-typography.md +433 -0
- package/content/skills/mobile-design/platform-android.md +666 -0
- package/content/skills/mobile-design/platform-ios.md +561 -0
- package/content/skills/mobile-design/scripts/mobile_audit.py +670 -0
- package/content/skills/mobile-design/touch-psychology.md +537 -0
- package/content/skills/nextjs-best-practices/SKILL.md +203 -0
- package/content/skills/nodejs-best-practices/SKILL.md +333 -0
- package/content/skills/parallel-agents/SKILL.md +175 -0
- package/content/skills/performance-profiling/SKILL.md +143 -0
- package/content/skills/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/content/skills/plan-writing/SKILL.md +152 -0
- package/content/skills/powershell-windows/SKILL.md +167 -0
- package/content/skills/python-patterns/SKILL.md +441 -0
- package/content/skills/react-patterns/SKILL.md +198 -0
- package/content/skills/red-team-tactics/SKILL.md +199 -0
- package/content/skills/seo-fundamentals/SKILL.md +129 -0
- package/content/skills/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/content/skills/server-management/SKILL.md +161 -0
- package/content/skills/systematic-debugging/SKILL.md +109 -0
- package/content/skills/tailwind-patterns/SKILL.md +269 -0
- package/content/skills/tdd-workflow/SKILL.md +149 -0
- package/content/skills/testing-patterns/SKILL.md +178 -0
- package/content/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/content/skills/vulnerability-scanner/SKILL.md +276 -0
- package/content/skills/vulnerability-scanner/checklists.md +121 -0
- package/content/skills/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/content/skills/webapp-testing/SKILL.md +187 -0
- package/content/skills/webapp-testing/scripts/playwright_runner.py +173 -0
- package/content/specialists/Especialista em Acessibilidade.md +266 -0
- package/content/specialists/Especialista em An/303/241lise de Testes.md" +434 -0
- package/content/specialists/Especialista em Arquitetura Avan/303/247ada.md" +358 -0
- package/content/specialists/Especialista em Arquitetura de Software.md +177 -0
- package/content/specialists/Especialista em Banco de Dados.md +260 -0
- package/content/specialists/Especialista em Contrato de API.md +172 -0
- package/content/specialists/Especialista em Dados e Analytics com IA.md +246 -0
- package/content/specialists/Especialista em Debugging e Troubleshooting.md +191 -0
- package/content/specialists/Especialista em Desenvolvimento Frontend.md +477 -0
- package/content/specialists/Especialista em Desenvolvimento Mobile.md +241 -0
- package/content/specialists/Especialista em Desenvolvimento e Vibe Coding Estruturado.md +417 -0
- package/content/specialists/Especialista em DevOps e Infraestrutura.md +294 -0
- package/content/specialists/Especialista em Documenta/303/247/303/243o T/303/251cnica.md" +227 -0
- package/content/specialists/Especialista em Engenharia de Requisitos com IA.md +299 -0
- package/content/specialists/Especialista em Explora/303/247/303/243o de Codebase.md" +179 -0
- package/content/specialists/Especialista em Gest/303/243o de Produto.md" +179 -0
- package/content/specialists/Especialista em Migra/303/247/303/243o e Moderniza/303/247/303/243o.md" +410 -0
- package/content/specialists/Especialista em Modelagem e Arquitetura de Dom/303/255nio com IA.md" +248 -0
- package/content/specialists/Especialista em Observabilidade.md +415 -0
- package/content/specialists/Especialista em Performance e Escalabilidade.md +373 -0
- package/content/specialists/Especialista em Plano de Execu/303/247/303/243o com IA.md" +341 -0
- package/content/specialists/Especialista em Prototipagem R/303/241pida com Google Stitch.md" +419 -0
- package/content/specialists/Especialista em Seguran/303/247a da Informa/303/247/303/243o.md" +508 -0
- package/content/specialists/Especialista em UX Design.md +453 -0
- package/content/specialists/INDEX.md +43 -0
- package/content/templates/PRD.md +165 -0
- package/content/templates/README.md +65 -0
- package/content/templates/adr.md +103 -0
- package/content/templates/arquitetura.md +279 -0
- package/content/templates/backlog.md +185 -0
- package/content/templates/checklist-seguranca.md +180 -0
- package/content/templates/contexto.md +120 -0
- package/content/templates/criterios-aceite.md +99 -0
- package/content/templates/design-banco.md +270 -0
- package/content/templates/design-doc.md +240 -0
- package/content/templates/feature.md +88 -0
- package/content/templates/historia-backend.md +84 -0
- package/content/templates/historia-frontend.md +75 -0
- package/content/templates/historia-usuario.md +125 -0
- package/content/templates/mapa-navegacao.md +133 -0
- package/content/templates/matriz-rastreabilidade.md +121 -0
- package/content/templates/modelo-dominio.md +219 -0
- package/content/templates/plano-testes.md +199 -0
- package/content/templates/prototipo-stitch.md +138 -0
- package/content/templates/requisitos.md +162 -0
- package/content/templates/slo-sli.md +197 -0
- package/content/workflows/README-MCP.md +363 -0
- package/content/workflows/brainstorm.md +113 -0
- package/content/workflows/create.md +59 -0
- package/content/workflows/debug.md +103 -0
- package/content/workflows/deploy.md +176 -0
- package/content/workflows/enhance.md +63 -0
- package/content/workflows/mcp-debug.md +506 -0
- package/content/workflows/mcp-feature.md +385 -0
- package/content/workflows/mcp-gate.md +413 -0
- package/content/workflows/mcp-next.md +388 -0
- package/content/workflows/mcp-refactor.md +600 -0
- package/content/workflows/mcp-start.md +304 -0
- package/content/workflows/mcp-status.md +400 -0
- package/content/workflows/orchestrate.md +237 -0
- package/content/workflows/plan.md +89 -0
- package/content/workflows/preview.md +81 -0
- package/content/workflows/status.md +86 -0
- package/content/workflows/test.md +144 -0
- package/content/workflows/ui-ux-pro-max.md +296 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.js +138 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.js +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +21 -0
- package/package.json +48 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# API Style Selection (2025)
|
|
2
|
+
|
|
3
|
+
> REST vs GraphQL vs tRPC - Hangi durumda hangisi?
|
|
4
|
+
|
|
5
|
+
## Decision Tree
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Who are the API consumers?
|
|
9
|
+
│
|
|
10
|
+
├── Public API / Multiple platforms
|
|
11
|
+
│ └── REST + OpenAPI (widest compatibility)
|
|
12
|
+
│
|
|
13
|
+
├── Complex data needs / Multiple frontends
|
|
14
|
+
│ └── GraphQL (flexible queries)
|
|
15
|
+
│
|
|
16
|
+
├── TypeScript frontend + backend (monorepo)
|
|
17
|
+
│ └── tRPC (end-to-end type safety)
|
|
18
|
+
│
|
|
19
|
+
├── Real-time / Event-driven
|
|
20
|
+
│ └── WebSocket + AsyncAPI
|
|
21
|
+
│
|
|
22
|
+
└── Internal microservices
|
|
23
|
+
└── gRPC (performance) or REST (simplicity)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Comparison
|
|
27
|
+
|
|
28
|
+
| Factor | REST | GraphQL | tRPC |
|
|
29
|
+
|--------|------|---------|------|
|
|
30
|
+
| **Best for** | Public APIs | Complex apps | TS monorepos |
|
|
31
|
+
| **Learning curve** | Low | Medium | Low (if TS) |
|
|
32
|
+
| **Over/under fetching** | Common | Solved | Solved |
|
|
33
|
+
| **Type safety** | Manual (OpenAPI) | Schema-based | Automatic |
|
|
34
|
+
| **Caching** | HTTP native | Complex | Client-based |
|
|
35
|
+
|
|
36
|
+
## Selection Questions
|
|
37
|
+
|
|
38
|
+
1. Who are the API consumers?
|
|
39
|
+
2. Is the frontend TypeScript?
|
|
40
|
+
3. How complex are the data relationships?
|
|
41
|
+
4. Is caching critical?
|
|
42
|
+
5. Public or internal API?
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Authentication Patterns
|
|
2
|
+
|
|
3
|
+
> Choose auth pattern based on use case.
|
|
4
|
+
|
|
5
|
+
## Selection Guide
|
|
6
|
+
|
|
7
|
+
| Pattern | Best For |
|
|
8
|
+
|---------|----------|
|
|
9
|
+
| **JWT** | Stateless, microservices |
|
|
10
|
+
| **Session** | Traditional web, simple |
|
|
11
|
+
| **OAuth 2.0** | Third-party integration |
|
|
12
|
+
| **API Keys** | Server-to-server, public APIs |
|
|
13
|
+
| **Passkey** | Modern passwordless (2025+) |
|
|
14
|
+
|
|
15
|
+
## JWT Principles
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Important:
|
|
19
|
+
├── Always verify signature
|
|
20
|
+
├── Check expiration
|
|
21
|
+
├── Include minimal claims
|
|
22
|
+
├── Use short expiry + refresh tokens
|
|
23
|
+
└── Never store sensitive data in JWT
|
|
24
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# API Documentation Principles
|
|
2
|
+
|
|
3
|
+
> Good docs = happy developers = API adoption.
|
|
4
|
+
|
|
5
|
+
## OpenAPI/Swagger Essentials
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Include:
|
|
9
|
+
├── All endpoints with examples
|
|
10
|
+
├── Request/response schemas
|
|
11
|
+
├── Authentication requirements
|
|
12
|
+
├── Error response formats
|
|
13
|
+
└── Rate limiting info
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Good Documentation Has
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Essentials:
|
|
20
|
+
├── Quick start / Getting started
|
|
21
|
+
├── Authentication guide
|
|
22
|
+
├── Complete API reference
|
|
23
|
+
├── Error handling guide
|
|
24
|
+
├── Code examples (multiple languages)
|
|
25
|
+
└── Changelog
|
|
26
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# GraphQL Principles
|
|
2
|
+
|
|
3
|
+
> Flexible queries for complex, interconnected data.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
✅ Good fit:
|
|
9
|
+
├── Complex, interconnected data
|
|
10
|
+
├── Multiple frontend platforms
|
|
11
|
+
├── Clients need flexible queries
|
|
12
|
+
├── Evolving data requirements
|
|
13
|
+
└── Reducing over-fetching matters
|
|
14
|
+
|
|
15
|
+
❌ Poor fit:
|
|
16
|
+
├── Simple CRUD operations
|
|
17
|
+
├── File upload heavy
|
|
18
|
+
├── HTTP caching important
|
|
19
|
+
└── Team unfamiliar with GraphQL
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Schema Design Principles
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Principles:
|
|
26
|
+
├── Think in graphs, not endpoints
|
|
27
|
+
├── Design for evolvability (no versions)
|
|
28
|
+
├── Use connections for pagination
|
|
29
|
+
├── Be specific with types (not generic "data")
|
|
30
|
+
└── Handle nullability thoughtfully
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Security Considerations
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Protect against:
|
|
37
|
+
├── Query depth attacks → Set max depth
|
|
38
|
+
├── Query complexity → Calculate cost
|
|
39
|
+
├── Batching abuse → Limit batch size
|
|
40
|
+
├── Introspection → Disable in production
|
|
41
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Rate Limiting Principles
|
|
2
|
+
|
|
3
|
+
> Protect your API from abuse and overload.
|
|
4
|
+
|
|
5
|
+
## Why Rate Limit
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Protect against:
|
|
9
|
+
├── Brute force attacks
|
|
10
|
+
├── Resource exhaustion
|
|
11
|
+
├── Cost overruns (if pay-per-use)
|
|
12
|
+
└── Unfair usage
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Strategy Selection
|
|
16
|
+
|
|
17
|
+
| Type | How | When |
|
|
18
|
+
|------|-----|------|
|
|
19
|
+
| **Token bucket** | Burst allowed, refills over time | Most APIs |
|
|
20
|
+
| **Sliding window** | Smooth distribution | Strict limits |
|
|
21
|
+
| **Fixed window** | Simple counters per window | Basic needs |
|
|
22
|
+
|
|
23
|
+
## Response Headers
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Include in headers:
|
|
27
|
+
├── X-RateLimit-Limit (max requests)
|
|
28
|
+
├── X-RateLimit-Remaining (requests left)
|
|
29
|
+
├── X-RateLimit-Reset (when limit resets)
|
|
30
|
+
└── Return 429 when exceeded
|
|
31
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Response Format Principles
|
|
2
|
+
|
|
3
|
+
> Consistency is key - choose a format and stick to it.
|
|
4
|
+
|
|
5
|
+
## Common Patterns
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Choose one:
|
|
9
|
+
├── Envelope pattern ({ success, data, error })
|
|
10
|
+
├── Direct data (just return the resource)
|
|
11
|
+
└── HAL/JSON:API (hypermedia)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Error Response
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Include:
|
|
18
|
+
├── Error code (for programmatic handling)
|
|
19
|
+
├── User message (for display)
|
|
20
|
+
├── Details (for debugging, field-level errors)
|
|
21
|
+
├── Request ID (for support)
|
|
22
|
+
└── NOT internal details (security!)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Pagination Types
|
|
26
|
+
|
|
27
|
+
| Type | Best For | Trade-offs |
|
|
28
|
+
|------|----------|------------|
|
|
29
|
+
| **Offset** | Simple, jumpable | Performance on large datasets |
|
|
30
|
+
| **Cursor** | Large datasets | Can't jump to page |
|
|
31
|
+
| **Keyset** | Performance critical | Requires sortable key |
|
|
32
|
+
|
|
33
|
+
### Selection Questions
|
|
34
|
+
|
|
35
|
+
1. How large is the dataset?
|
|
36
|
+
2. Do users need to jump to specific pages?
|
|
37
|
+
3. Is data frequently changing?
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# REST Principles
|
|
2
|
+
|
|
3
|
+
> Resource-based API design - nouns not verbs.
|
|
4
|
+
|
|
5
|
+
## Resource Naming Rules
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Principles:
|
|
9
|
+
├── Use NOUNS, not verbs (resources, not actions)
|
|
10
|
+
├── Use PLURAL forms (/users not /user)
|
|
11
|
+
├── Use lowercase with hyphens (/user-profiles)
|
|
12
|
+
├── Nest for relationships (/users/123/posts)
|
|
13
|
+
└── Keep shallow (max 3 levels deep)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## HTTP Method Selection
|
|
17
|
+
|
|
18
|
+
| Method | Purpose | Idempotent? | Body? |
|
|
19
|
+
|--------|---------|-------------|-------|
|
|
20
|
+
| **GET** | Read resource(s) | Yes | No |
|
|
21
|
+
| **POST** | Create new resource | No | Yes |
|
|
22
|
+
| **PUT** | Replace entire resource | Yes | Yes |
|
|
23
|
+
| **PATCH** | Partial update | No | Yes |
|
|
24
|
+
| **DELETE** | Remove resource | Yes | No |
|
|
25
|
+
|
|
26
|
+
## Status Code Selection
|
|
27
|
+
|
|
28
|
+
| Situation | Code | Why |
|
|
29
|
+
|-----------|------|-----|
|
|
30
|
+
| Success (read) | 200 | Standard success |
|
|
31
|
+
| Created | 201 | New resource created |
|
|
32
|
+
| No content | 204 | Success, nothing to return |
|
|
33
|
+
| Bad request | 400 | Malformed request |
|
|
34
|
+
| Unauthorized | 401 | Missing/invalid auth |
|
|
35
|
+
| Forbidden | 403 | Valid auth, no permission |
|
|
36
|
+
| Not found | 404 | Resource doesn't exist |
|
|
37
|
+
| Conflict | 409 | State conflict (duplicate) |
|
|
38
|
+
| Validation error | 422 | Valid syntax, invalid data |
|
|
39
|
+
| Rate limited | 429 | Too many requests |
|
|
40
|
+
| Server error | 500 | Our fault |
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
API Validator - Checks API endpoints for best practices.
|
|
4
|
+
Validates OpenAPI specs, response formats, and common issues.
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
# Fix Windows console encoding for Unicode output
|
|
12
|
+
try:
|
|
13
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
14
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
15
|
+
except AttributeError:
|
|
16
|
+
pass # Python < 3.7
|
|
17
|
+
|
|
18
|
+
def find_api_files(project_path: Path) -> list:
|
|
19
|
+
"""Find API-related files."""
|
|
20
|
+
patterns = [
|
|
21
|
+
"**/*api*.ts", "**/*api*.js", "**/*api*.py",
|
|
22
|
+
"**/routes/*.ts", "**/routes/*.js", "**/routes/*.py",
|
|
23
|
+
"**/controllers/*.ts", "**/controllers/*.js",
|
|
24
|
+
"**/endpoints/*.ts", "**/endpoints/*.py",
|
|
25
|
+
"**/*.openapi.json", "**/*.openapi.yaml",
|
|
26
|
+
"**/swagger.json", "**/swagger.yaml",
|
|
27
|
+
"**/openapi.json", "**/openapi.yaml"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
files = []
|
|
31
|
+
for pattern in patterns:
|
|
32
|
+
files.extend(project_path.glob(pattern))
|
|
33
|
+
|
|
34
|
+
# Exclude node_modules, etc.
|
|
35
|
+
return [f for f in files if not any(x in str(f) for x in ['node_modules', '.git', 'dist', 'build', '__pycache__'])]
|
|
36
|
+
|
|
37
|
+
def check_openapi_spec(file_path: Path) -> dict:
|
|
38
|
+
"""Check OpenAPI/Swagger specification."""
|
|
39
|
+
issues = []
|
|
40
|
+
passed = []
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
content = file_path.read_text(encoding='utf-8')
|
|
44
|
+
|
|
45
|
+
if file_path.suffix == '.json':
|
|
46
|
+
spec = json.loads(content)
|
|
47
|
+
else:
|
|
48
|
+
# Basic YAML check
|
|
49
|
+
if 'openapi:' in content or 'swagger:' in content:
|
|
50
|
+
passed.append("[OK] OpenAPI/Swagger version defined")
|
|
51
|
+
else:
|
|
52
|
+
issues.append("[X] No OpenAPI version found")
|
|
53
|
+
|
|
54
|
+
if 'paths:' in content:
|
|
55
|
+
passed.append("[OK] Paths section exists")
|
|
56
|
+
else:
|
|
57
|
+
issues.append("[X] No paths defined")
|
|
58
|
+
|
|
59
|
+
if 'components:' in content or 'definitions:' in content:
|
|
60
|
+
passed.append("[OK] Schema components defined")
|
|
61
|
+
|
|
62
|
+
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'openapi'}
|
|
63
|
+
|
|
64
|
+
# JSON OpenAPI checks
|
|
65
|
+
if 'openapi' in spec or 'swagger' in spec:
|
|
66
|
+
passed.append("[OK] OpenAPI version defined")
|
|
67
|
+
|
|
68
|
+
if 'info' in spec:
|
|
69
|
+
if 'title' in spec['info']:
|
|
70
|
+
passed.append("[OK] API title defined")
|
|
71
|
+
if 'version' in spec['info']:
|
|
72
|
+
passed.append("[OK] API version defined")
|
|
73
|
+
if 'description' not in spec['info']:
|
|
74
|
+
issues.append("[!] API description missing")
|
|
75
|
+
|
|
76
|
+
if 'paths' in spec:
|
|
77
|
+
path_count = len(spec['paths'])
|
|
78
|
+
passed.append(f"[OK] {path_count} endpoints defined")
|
|
79
|
+
|
|
80
|
+
# Check each path
|
|
81
|
+
for path, methods in spec['paths'].items():
|
|
82
|
+
for method, details in methods.items():
|
|
83
|
+
if method in ['get', 'post', 'put', 'patch', 'delete']:
|
|
84
|
+
if 'responses' not in details:
|
|
85
|
+
issues.append(f"[X] {method.upper()} {path}: No responses defined")
|
|
86
|
+
if 'summary' not in details and 'description' not in details:
|
|
87
|
+
issues.append(f"[!] {method.upper()} {path}: No description")
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
issues.append(f"[X] Parse error: {e}")
|
|
91
|
+
|
|
92
|
+
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'openapi'}
|
|
93
|
+
|
|
94
|
+
def check_api_code(file_path: Path) -> dict:
|
|
95
|
+
"""Check API code for common issues."""
|
|
96
|
+
issues = []
|
|
97
|
+
passed = []
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
content = file_path.read_text(encoding='utf-8')
|
|
101
|
+
|
|
102
|
+
# Check for error handling
|
|
103
|
+
error_patterns = [
|
|
104
|
+
r'try\s*{', r'try:', r'\.catch\(',
|
|
105
|
+
r'except\s+', r'catch\s*\('
|
|
106
|
+
]
|
|
107
|
+
has_error_handling = any(re.search(p, content) for p in error_patterns)
|
|
108
|
+
if has_error_handling:
|
|
109
|
+
passed.append("[OK] Error handling present")
|
|
110
|
+
else:
|
|
111
|
+
issues.append("[X] No error handling found")
|
|
112
|
+
|
|
113
|
+
# Check for status codes
|
|
114
|
+
status_patterns = [
|
|
115
|
+
r'status\s*\(\s*\d{3}\s*\)', r'statusCode\s*[=:]\s*\d{3}',
|
|
116
|
+
r'HttpStatus\.', r'status_code\s*=\s*\d{3}',
|
|
117
|
+
r'\.status\(\d{3}\)', r'res\.status\('
|
|
118
|
+
]
|
|
119
|
+
has_status = any(re.search(p, content) for p in status_patterns)
|
|
120
|
+
if has_status:
|
|
121
|
+
passed.append("[OK] HTTP status codes used")
|
|
122
|
+
else:
|
|
123
|
+
issues.append("[!] No explicit HTTP status codes")
|
|
124
|
+
|
|
125
|
+
# Check for validation
|
|
126
|
+
validation_patterns = [
|
|
127
|
+
r'validate', r'schema', r'zod', r'joi', r'yup',
|
|
128
|
+
r'pydantic', r'@Body\(', r'@Query\('
|
|
129
|
+
]
|
|
130
|
+
has_validation = any(re.search(p, content, re.I) for p in validation_patterns)
|
|
131
|
+
if has_validation:
|
|
132
|
+
passed.append("[OK] Input validation present")
|
|
133
|
+
else:
|
|
134
|
+
issues.append("[!] No input validation detected")
|
|
135
|
+
|
|
136
|
+
# Check for auth middleware
|
|
137
|
+
auth_patterns = [
|
|
138
|
+
r'auth', r'jwt', r'bearer', r'token',
|
|
139
|
+
r'middleware', r'guard', r'@Authenticated'
|
|
140
|
+
]
|
|
141
|
+
has_auth = any(re.search(p, content, re.I) for p in auth_patterns)
|
|
142
|
+
if has_auth:
|
|
143
|
+
passed.append("[OK] Authentication/authorization detected")
|
|
144
|
+
|
|
145
|
+
# Check for rate limiting
|
|
146
|
+
rate_patterns = [r'rateLimit', r'throttle', r'rate.?limit']
|
|
147
|
+
has_rate = any(re.search(p, content, re.I) for p in rate_patterns)
|
|
148
|
+
if has_rate:
|
|
149
|
+
passed.append("[OK] Rate limiting present")
|
|
150
|
+
|
|
151
|
+
# Check for logging
|
|
152
|
+
log_patterns = [r'console\.log', r'logger\.', r'logging\.', r'log\.']
|
|
153
|
+
has_logging = any(re.search(p, content) for p in log_patterns)
|
|
154
|
+
if has_logging:
|
|
155
|
+
passed.append("[OK] Logging present")
|
|
156
|
+
|
|
157
|
+
except Exception as e:
|
|
158
|
+
issues.append(f"[X] Read error: {e}")
|
|
159
|
+
|
|
160
|
+
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'code'}
|
|
161
|
+
|
|
162
|
+
def main():
|
|
163
|
+
target = sys.argv[1] if len(sys.argv) > 1 else "."
|
|
164
|
+
project_path = Path(target)
|
|
165
|
+
|
|
166
|
+
print("\n" + "=" * 60)
|
|
167
|
+
print(" API VALIDATOR - Endpoint Best Practices Check")
|
|
168
|
+
print("=" * 60 + "\n")
|
|
169
|
+
|
|
170
|
+
api_files = find_api_files(project_path)
|
|
171
|
+
|
|
172
|
+
if not api_files:
|
|
173
|
+
print("[!] No API files found.")
|
|
174
|
+
print(" Looking for: routes/, controllers/, api/, openapi.json/yaml")
|
|
175
|
+
sys.exit(0)
|
|
176
|
+
|
|
177
|
+
results = []
|
|
178
|
+
for file_path in api_files[:15]: # Limit
|
|
179
|
+
if 'openapi' in file_path.name.lower() or 'swagger' in file_path.name.lower():
|
|
180
|
+
result = check_openapi_spec(file_path)
|
|
181
|
+
else:
|
|
182
|
+
result = check_api_code(file_path)
|
|
183
|
+
results.append(result)
|
|
184
|
+
|
|
185
|
+
# Print results
|
|
186
|
+
total_issues = 0
|
|
187
|
+
total_passed = 0
|
|
188
|
+
|
|
189
|
+
for result in results:
|
|
190
|
+
print(f"\n[FILE] {result['file']} [{result['type']}]")
|
|
191
|
+
for item in result['passed']:
|
|
192
|
+
print(f" {item}")
|
|
193
|
+
total_passed += 1
|
|
194
|
+
for item in result['issues']:
|
|
195
|
+
print(f" {item}")
|
|
196
|
+
if item.startswith("[X]"):
|
|
197
|
+
total_issues += 1
|
|
198
|
+
|
|
199
|
+
print("\n" + "=" * 60)
|
|
200
|
+
print(f"[RESULTS] {total_passed} passed, {total_issues} critical issues")
|
|
201
|
+
print("=" * 60)
|
|
202
|
+
|
|
203
|
+
if total_issues == 0:
|
|
204
|
+
print("[OK] API validation passed")
|
|
205
|
+
sys.exit(0)
|
|
206
|
+
else:
|
|
207
|
+
print("[X] Fix critical issues before deployment")
|
|
208
|
+
sys.exit(1)
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
main()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# API Security Testing
|
|
2
|
+
|
|
3
|
+
> Principles for testing API security. OWASP API Top 10, authentication, authorization testing.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## OWASP API Security Top 10
|
|
8
|
+
|
|
9
|
+
| Vulnerability | Test Focus |
|
|
10
|
+
|---------------|------------|
|
|
11
|
+
| **API1: BOLA** | Access other users' resources |
|
|
12
|
+
| **API2: Broken Auth** | JWT, session, credentials |
|
|
13
|
+
| **API3: Property Auth** | Mass assignment, data exposure |
|
|
14
|
+
| **API4: Resource Consumption** | Rate limiting, DoS |
|
|
15
|
+
| **API5: Function Auth** | Admin endpoints, role bypass |
|
|
16
|
+
| **API6: Business Flow** | Logic abuse, automation |
|
|
17
|
+
| **API7: SSRF** | Internal network access |
|
|
18
|
+
| **API8: Misconfiguration** | Debug endpoints, CORS |
|
|
19
|
+
| **API9: Inventory** | Shadow APIs, old versions |
|
|
20
|
+
| **API10: Unsafe Consumption** | Third-party API trust |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Authentication Testing
|
|
25
|
+
|
|
26
|
+
### JWT Testing
|
|
27
|
+
|
|
28
|
+
| Check | What to Test |
|
|
29
|
+
|-------|--------------|
|
|
30
|
+
| Algorithm | None, algorithm confusion |
|
|
31
|
+
| Secret | Weak secrets, brute force |
|
|
32
|
+
| Claims | Expiration, issuer, audience |
|
|
33
|
+
| Signature | Manipulation, key injection |
|
|
34
|
+
|
|
35
|
+
### Session Testing
|
|
36
|
+
|
|
37
|
+
| Check | What to Test |
|
|
38
|
+
|-------|--------------|
|
|
39
|
+
| Generation | Predictability |
|
|
40
|
+
| Storage | Client-side security |
|
|
41
|
+
| Expiration | Timeout enforcement |
|
|
42
|
+
| Invalidation | Logout effectiveness |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Authorization Testing
|
|
47
|
+
|
|
48
|
+
| Test Type | Approach |
|
|
49
|
+
|-----------|----------|
|
|
50
|
+
| **Horizontal** | Access peer users' data |
|
|
51
|
+
| **Vertical** | Access higher privilege functions |
|
|
52
|
+
| **Context** | Access outside allowed scope |
|
|
53
|
+
|
|
54
|
+
### BOLA/IDOR Testing
|
|
55
|
+
|
|
56
|
+
1. Identify resource IDs in requests
|
|
57
|
+
2. Capture request with user A's session
|
|
58
|
+
3. Replay with user B's session
|
|
59
|
+
4. Check for unauthorized access
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Input Validation Testing
|
|
64
|
+
|
|
65
|
+
| Injection Type | Test Focus |
|
|
66
|
+
|----------------|------------|
|
|
67
|
+
| SQL | Query manipulation |
|
|
68
|
+
| NoSQL | Document queries |
|
|
69
|
+
| Command | System commands |
|
|
70
|
+
| LDAP | Directory queries |
|
|
71
|
+
|
|
72
|
+
**Approach:** Test all parameters, try type coercion, test boundaries, check error messages.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Rate Limiting Testing
|
|
77
|
+
|
|
78
|
+
| Aspect | Check |
|
|
79
|
+
|--------|-------|
|
|
80
|
+
| Existence | Is there any limit? |
|
|
81
|
+
| Bypass | Headers, IP rotation |
|
|
82
|
+
| Scope | Per-user, per-IP, global |
|
|
83
|
+
|
|
84
|
+
**Bypass techniques:** X-Forwarded-For, different HTTP methods, case variations, API versioning.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## GraphQL Security
|
|
89
|
+
|
|
90
|
+
| Test | Focus |
|
|
91
|
+
|------|-------|
|
|
92
|
+
| Introspection | Schema disclosure |
|
|
93
|
+
| Batching | Query DoS |
|
|
94
|
+
| Nesting | Depth-based DoS |
|
|
95
|
+
| Authorization | Field-level access |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Security Testing Checklist
|
|
100
|
+
|
|
101
|
+
**Authentication:**
|
|
102
|
+
- [ ] Test for bypass
|
|
103
|
+
- [ ] Check credential strength
|
|
104
|
+
- [ ] Verify token security
|
|
105
|
+
|
|
106
|
+
**Authorization:**
|
|
107
|
+
- [ ] Test BOLA/IDOR
|
|
108
|
+
- [ ] Check privilege escalation
|
|
109
|
+
- [ ] Verify function access
|
|
110
|
+
|
|
111
|
+
**Input:**
|
|
112
|
+
- [ ] Test all parameters
|
|
113
|
+
- [ ] Check for injection
|
|
114
|
+
|
|
115
|
+
**Config:**
|
|
116
|
+
- [ ] Check CORS
|
|
117
|
+
- [ ] Verify headers
|
|
118
|
+
- [ ] Test error handling
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
> **Remember:** APIs are the backbone of modern apps. Test them like attackers will.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# tRPC Principles
|
|
2
|
+
|
|
3
|
+
> End-to-end type safety for TypeScript monorepos.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
✅ Perfect fit:
|
|
9
|
+
├── TypeScript on both ends
|
|
10
|
+
├── Monorepo structure
|
|
11
|
+
├── Internal tools
|
|
12
|
+
├── Rapid development
|
|
13
|
+
└── Type safety critical
|
|
14
|
+
|
|
15
|
+
❌ Poor fit:
|
|
16
|
+
├── Non-TypeScript clients
|
|
17
|
+
├── Public API
|
|
18
|
+
├── Need REST conventions
|
|
19
|
+
└── Multiple language backends
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Key Benefits
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Why tRPC:
|
|
26
|
+
├── Zero schema maintenance
|
|
27
|
+
├── End-to-end type inference
|
|
28
|
+
├── IDE autocomplete across stack
|
|
29
|
+
├── Instant API changes reflected
|
|
30
|
+
└── No code generation step
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Integration Patterns
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Common setups:
|
|
37
|
+
├── Next.js + tRPC (most common)
|
|
38
|
+
├── Monorepo with shared types
|
|
39
|
+
├── Remix + tRPC
|
|
40
|
+
└── Any TS frontend + backend
|
|
41
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Versioning Strategies
|
|
2
|
+
|
|
3
|
+
> Plan for API evolution from day one.
|
|
4
|
+
|
|
5
|
+
## Decision Factors
|
|
6
|
+
|
|
7
|
+
| Strategy | Implementation | Trade-offs |
|
|
8
|
+
|----------|---------------|------------|
|
|
9
|
+
| **URI** | /v1/users | Clear, easy caching |
|
|
10
|
+
| **Header** | Accept-Version: 1 | Cleaner URLs, harder discovery |
|
|
11
|
+
| **Query** | ?version=1 | Easy to add, messy |
|
|
12
|
+
| **None** | Evolve carefully | Best for internal, risky for public |
|
|
13
|
+
|
|
14
|
+
## Versioning Philosophy
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Consider:
|
|
18
|
+
├── Public API? → Version in URI
|
|
19
|
+
├── Internal only? → May not need versioning
|
|
20
|
+
├── GraphQL? → Typically no versions (evolve schema)
|
|
21
|
+
├── tRPC? → Types enforce compatibility
|
|
22
|
+
```
|