@saulwade/swl-ses 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/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-experto
|
|
3
|
+
description: >
|
|
4
|
+
NestJS con TypeScript: módulos, controladores, servicios, DTOs con class-validator,
|
|
5
|
+
guards JWT, interceptors, TypeORM y Prisma. Cargar cuando se implementen APIs
|
|
6
|
+
NestJS, módulos con inyección de dependencias, autenticación JWT/Passport,
|
|
7
|
+
endpoints REST o GraphQL con NestJS, o configuración de pipes y guards.
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
herramientasPermitidas: [Read]
|
|
10
|
+
exclusiones:
|
|
11
|
+
- "No cargar para APIs Express o Fastify sin NestJS — la DI, los decoradores y el sistema de módulos son exclusivos de NestJS; para Node.js sin framework opinionado cargar `node-experto`."
|
|
12
|
+
- "No cargar para el frontend Next.js aunque comparten TypeScript — Next.js y NestJS son backends distintos con modelos de rendering y lifecycle diferentes; cargar `nextjs-experto`."
|
|
13
|
+
- "No cargar para la configuración de TypeORM o Prisma independientemente de NestJS — si la pregunta es sobre migraciones o queries del ORM en un proyecto no-NestJS, cargar el skill del ORM directamente."
|
|
14
|
+
- "No cargar para errores de compilación TypeScript en proyectos NestJS — esos son errores TS genéricos; cargar `build-errors-typescript` o `typescript-diagnosticos`."
|
|
15
|
+
evolvable: true # default para skill estandar
|
|
16
|
+
---
|
|
17
|
+
# NestJS Experto — APIs TypeScript de Producción
|
|
18
|
+
|
|
19
|
+
## Cuándo NO cargar
|
|
20
|
+
|
|
21
|
+
- El proyecto usa Express o Fastify sin NestJS — para Node.js sin framework opinionado cargar `node-experto`.
|
|
22
|
+
- La app es Next.js frontend — NestJS y Next.js son backends distintos; cargar `nextjs-experto`.
|
|
23
|
+
- Los errores son de tsconfig o tsc — cargar `build-errors-typescript`.
|
|
24
|
+
|
|
25
|
+
## Estructura de proyecto
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
src/
|
|
29
|
+
├── app.module.ts
|
|
30
|
+
├── main.ts
|
|
31
|
+
├── common/
|
|
32
|
+
│ ├── decorators/
|
|
33
|
+
│ ├── filters/
|
|
34
|
+
│ ├── guards/
|
|
35
|
+
│ ├── interceptors/
|
|
36
|
+
│ └── pipes/
|
|
37
|
+
├── config/
|
|
38
|
+
│ └── configuration.ts # ConfigModule con validación Joi/Zod
|
|
39
|
+
└── modules/
|
|
40
|
+
└── users/
|
|
41
|
+
├── users.module.ts
|
|
42
|
+
├── users.controller.ts
|
|
43
|
+
├── users.service.ts
|
|
44
|
+
├── dto/
|
|
45
|
+
│ ├── create-user.dto.ts
|
|
46
|
+
│ └── update-user.dto.ts
|
|
47
|
+
└── entities/
|
|
48
|
+
└── user.entity.ts
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Regla idiomática crítica: SIEMPRE constructor injection
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// NUNCA — field injection
|
|
57
|
+
@Injectable()
|
|
58
|
+
export class UsersService {
|
|
59
|
+
@InjectRepository(User)
|
|
60
|
+
private usersRepository: Repository<User>; // MAL: dificulta testing y viola DI explícita
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// SIEMPRE — constructor injection
|
|
64
|
+
@Injectable()
|
|
65
|
+
export class UsersService {
|
|
66
|
+
constructor(
|
|
67
|
+
@InjectRepository(User)
|
|
68
|
+
private readonly usersRepository: Repository<User>,
|
|
69
|
+
) {}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## DTO con class-validator y class-transformer
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
|
|
79
|
+
import { Transform } from 'class-transformer';
|
|
80
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
81
|
+
|
|
82
|
+
export class CreateUserDto {
|
|
83
|
+
@ApiProperty({ example: 'usuario@ejemplo.com' })
|
|
84
|
+
@IsEmail({}, { message: 'El correo no es válido' })
|
|
85
|
+
@Transform(({ value }: { value: unknown }) => {
|
|
86
|
+
if (typeof value === 'string') return value.toLowerCase().trim();
|
|
87
|
+
return value;
|
|
88
|
+
})
|
|
89
|
+
email!: string;
|
|
90
|
+
|
|
91
|
+
@ApiProperty({ example: 'contraseñaSegura123', minLength: 8 })
|
|
92
|
+
@IsString()
|
|
93
|
+
@MinLength(8, { message: 'La contraseña debe tener mínimo 8 caracteres' })
|
|
94
|
+
password!: string;
|
|
95
|
+
|
|
96
|
+
@ApiProperty({ required: false })
|
|
97
|
+
@IsOptional()
|
|
98
|
+
@IsString()
|
|
99
|
+
nombre?: string;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Controller con Swagger y decoradores correctos
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import {
|
|
109
|
+
Controller, Get, Post, Body, Param, HttpCode, HttpStatus, UseGuards,
|
|
110
|
+
} from '@nestjs/common';
|
|
111
|
+
import {
|
|
112
|
+
ApiTags, ApiBearerAuth, ApiCreatedResponse, ApiOkResponse,
|
|
113
|
+
ApiNotFoundResponse, ApiBadRequestResponse,
|
|
114
|
+
} from '@nestjs/swagger';
|
|
115
|
+
import { ParseUUIDPipe } from '@nestjs/common';
|
|
116
|
+
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
|
117
|
+
import { UsersService } from './users.service';
|
|
118
|
+
import { CreateUserDto } from './dto/create-user.dto';
|
|
119
|
+
import { UserResponseDto } from './dto/user-response.dto';
|
|
120
|
+
|
|
121
|
+
@ApiTags('usuarios')
|
|
122
|
+
@ApiBearerAuth()
|
|
123
|
+
@UseGuards(JwtAuthGuard)
|
|
124
|
+
@Controller('usuarios')
|
|
125
|
+
export class UsersController {
|
|
126
|
+
constructor(private readonly usersService: UsersService) {}
|
|
127
|
+
|
|
128
|
+
@Post()
|
|
129
|
+
@HttpCode(HttpStatus.CREATED)
|
|
130
|
+
@ApiCreatedResponse({ type: UserResponseDto, description: 'Usuario creado' })
|
|
131
|
+
@ApiBadRequestResponse({ description: 'Datos de entrada inválidos' })
|
|
132
|
+
async crear(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
|
|
133
|
+
return this.usersService.crear(dto);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Get(':id')
|
|
137
|
+
@ApiOkResponse({ type: UserResponseDto })
|
|
138
|
+
@ApiNotFoundResponse({ description: 'Usuario no encontrado' })
|
|
139
|
+
async obtener(
|
|
140
|
+
@Param('id', ParseUUIDPipe) id: string,
|
|
141
|
+
): Promise<UserResponseDto> {
|
|
142
|
+
return this.usersService.obtenerPorId(id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Service con manejo de errores tipado
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import {
|
|
153
|
+
Injectable, NotFoundException, ConflictException,
|
|
154
|
+
} from '@nestjs/common';
|
|
155
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
156
|
+
import { Repository } from 'typeorm';
|
|
157
|
+
import * as bcrypt from 'bcrypt';
|
|
158
|
+
import { User } from './entities/user.entity';
|
|
159
|
+
import { CreateUserDto } from './dto/create-user.dto';
|
|
160
|
+
|
|
161
|
+
@Injectable()
|
|
162
|
+
export class UsersService {
|
|
163
|
+
constructor(
|
|
164
|
+
@InjectRepository(User)
|
|
165
|
+
private readonly usersRepository: Repository<User>,
|
|
166
|
+
) {}
|
|
167
|
+
|
|
168
|
+
async crear(dto: CreateUserDto): Promise<User> {
|
|
169
|
+
const existe = await this.usersRepository.findOneBy({ email: dto.email });
|
|
170
|
+
if (existe) {
|
|
171
|
+
throw new ConflictException('El correo ya está registrado');
|
|
172
|
+
}
|
|
173
|
+
const hashedPassword = await bcrypt.hash(dto.password, 12);
|
|
174
|
+
const usuario = this.usersRepository.create({
|
|
175
|
+
...dto,
|
|
176
|
+
password: hashedPassword,
|
|
177
|
+
});
|
|
178
|
+
return this.usersRepository.save(usuario);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async obtenerPorId(id: string): Promise<User> {
|
|
182
|
+
const usuario = await this.usersRepository.findOneBy({ id });
|
|
183
|
+
if (!usuario) {
|
|
184
|
+
throw new NotFoundException(`Usuario ${id} no encontrado`);
|
|
185
|
+
}
|
|
186
|
+
return usuario;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Module con exports correctos
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { Module } from '@nestjs/common';
|
|
197
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
198
|
+
import { JwtModule } from '@nestjs/jwt';
|
|
199
|
+
import { ConfigService } from '@nestjs/config';
|
|
200
|
+
import { User } from './entities/user.entity';
|
|
201
|
+
import { UsersController } from './users.controller';
|
|
202
|
+
import { UsersService } from './users.service';
|
|
203
|
+
import { JwtStrategy } from '../common/guards/jwt.strategy';
|
|
204
|
+
|
|
205
|
+
@Module({
|
|
206
|
+
imports: [
|
|
207
|
+
TypeOrmModule.forFeature([User]),
|
|
208
|
+
JwtModule.registerAsync({
|
|
209
|
+
inject: [ConfigService],
|
|
210
|
+
useFactory: (config: ConfigService) => ({
|
|
211
|
+
secret: config.getOrThrow<string>('JWT_SECRET'),
|
|
212
|
+
signOptions: { expiresIn: '7d' },
|
|
213
|
+
}),
|
|
214
|
+
}),
|
|
215
|
+
],
|
|
216
|
+
controllers: [UsersController],
|
|
217
|
+
providers: [UsersService, JwtStrategy],
|
|
218
|
+
exports: [UsersService], // Exportar solo lo que otros módulos necesitan
|
|
219
|
+
})
|
|
220
|
+
export class UsersModule {}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## GlobalPipe y configuración de main.ts
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { NestFactory } from '@nestjs/core';
|
|
229
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
230
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
|
231
|
+
import { AppModule } from './app.module';
|
|
232
|
+
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
|
233
|
+
|
|
234
|
+
async function bootstrap(): Promise<void> {
|
|
235
|
+
const app = await NestFactory.create(AppModule);
|
|
236
|
+
|
|
237
|
+
app.useGlobalPipes(
|
|
238
|
+
new ValidationPipe({
|
|
239
|
+
whitelist: true, // Elimina propiedades no declaradas en DTO
|
|
240
|
+
forbidNonWhitelisted: true, // Lanza error si llegan propiedades extra
|
|
241
|
+
transform: true, // Transforma strings a tipos correctos
|
|
242
|
+
transformOptions: { enableImplicitConversion: true },
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
app.useGlobalFilters(new HttpExceptionFilter());
|
|
247
|
+
app.enableCors({
|
|
248
|
+
origin: process.env['CORS_ORIGIN']?.split(',') ?? [],
|
|
249
|
+
credentials: true,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const config = new DocumentBuilder()
|
|
253
|
+
.setTitle('API')
|
|
254
|
+
.setVersion('1.0')
|
|
255
|
+
.addBearerAuth()
|
|
256
|
+
.build();
|
|
257
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
258
|
+
SwaggerModule.setup('docs', app, document);
|
|
259
|
+
|
|
260
|
+
const port = process.env['PORT'] ?? 3000;
|
|
261
|
+
await app.listen(port);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
void bootstrap();
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## MUST DO / MUST NOT DO
|
|
270
|
+
|
|
271
|
+
**MUST DO:**
|
|
272
|
+
- Constructor injection siempre (nunca field injection)
|
|
273
|
+
- Validar inputs con class-validator en todos los DTOs
|
|
274
|
+
- `whitelist: true` y `forbidNonWhitelisted: true` en ValidationPipe global
|
|
275
|
+
- Tipos explícitos en todas las funciones — cero `any`
|
|
276
|
+
- `getOrThrow` en ConfigService para variables de entorno requeridas
|
|
277
|
+
- Lanzar excepciones tipadas de `@nestjs/common`: NotFoundException, ConflictException, UnauthorizedException, ForbiddenException
|
|
278
|
+
- Documentar endpoints con @ApiTags, @ApiOperation, @ApiResponse
|
|
279
|
+
|
|
280
|
+
**MUST NOT DO:**
|
|
281
|
+
- Field injection con @Inject sin constructor
|
|
282
|
+
- `any` como tipo — usar `unknown` + type guards
|
|
283
|
+
- Lógica de negocio en controllers
|
|
284
|
+
- Hardcodear secrets — usar ConfigModule con validación al arranque
|
|
285
|
+
- Circular dependencies entre módulos — usar forwardRef() solo si es inevitable
|
|
286
|
+
- Olvidar `exports` en módulos cuando otro módulo necesita el servicio
|
|
287
|
+
- Omitir `readonly` en dependencias inyectadas del constructor
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Gotchas / Errores comunes no obvios
|
|
292
|
+
|
|
293
|
+
**Dependencia circular silenciosa entre módulos**: cuando el módulo A importa al módulo B y viceversa, NestJS no lanza un error claro — la aplicación arranca pero algunas dependencias son `undefined`. Causa: NestJS no puede resolver el orden de inicialización en un ciclo. Fix: extraer la dependencia compartida a un módulo C que ambos importen, o usar `forwardRef(() => ModuloB)` + `@Inject(forwardRef(() => ServicioB))` en el constructor si el ciclo es inevitable.
|
|
294
|
+
|
|
295
|
+
**`@InjectRepository` fuera del módulo que declara `TypeOrmModule.forFeature`**: el servicio arranca pero falla con `No repository for "Entidad" was found` en runtime. Causa: `@InjectRepository(Entidad)` requiere que `TypeOrmModule.forFeature([Entidad])` esté en el mismo módulo o que el módulo que lo incluye esté en `exports`. Fix: verificar que el módulo que usa el repositorio tiene `TypeOrmModule.forFeature([Entidad])` en sus `imports`, o que lo importa de otro módulo que lo exporta.
|
|
296
|
+
|
|
297
|
+
**`ConfigService.get()` retorna `undefined` en guards y middlewares**: ocurre cuando el guard se registra globalmente con `APP_GUARD` antes de que `ConfigModule` termine de inicializar. Causa: orden de inicialización en el contenedor DI. Fix: usar `ConfigService.getOrThrow()` para detectarlo temprano, y verificar que `ConfigModule.forRoot({ isGlobal: true })` está en `AppModule` como primer import.
|
|
298
|
+
|
|
299
|
+
**ValidationPipe con `transform: true` no convierte tipos en `@Param`**: el parámetro `id` llega como `string` aunque el DTO declare `@IsNumber()`. Causa: `@Param('id')` extrae strings del path — `transform: true` convierte con `class-transformer` pero el valor del path ya es string y no se convierte a number automáticamente sin `@Type(() => Number)`. Fix: agregar `@Type(() => Number)` en el DTO, o usar `ParseIntPipe` / `ParseUUIDPipe` directamente en el decorador `@Param`.
|
|
300
|
+
|
|
301
|
+
## Tabla de referencias
|
|
302
|
+
|
|
303
|
+
| Tema | Archivo |
|
|
304
|
+
|------|---------|
|
|
305
|
+
| Módulos avanzados, DI circular, forwardRef, scopes, dynamic modules | recursos/modulos-di.md |
|
|
306
|
+
| Guards JWT, interceptors de logging/caché, exception filters, throttling | recursos/guards-interceptors.md |
|
|
307
|
+
| Testing con Jest, Test.createTestingModule, mocks de repositorios, e2e | recursos/testing-nestjs.md |
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# Guards, Interceptors, Filters y Pipes Avanzados en NestJS
|
|
2
|
+
|
|
3
|
+
## JwtAuthGuard con @Public() decorator
|
|
4
|
+
|
|
5
|
+
El patrón con Reflector permite marcar rutas públicas sin quitar el guard global.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// common/decorators/public.decorator.ts
|
|
9
|
+
import { SetMetadata } from '@nestjs/common';
|
|
10
|
+
|
|
11
|
+
export const IS_PUBLIC_KEY = 'isPublic';
|
|
12
|
+
export const Public = (): MethodDecorator & ClassDecorator =>
|
|
13
|
+
SetMetadata(IS_PUBLIC_KEY, true);
|
|
14
|
+
|
|
15
|
+
// common/guards/jwt-auth.guard.ts
|
|
16
|
+
import { Injectable, ExecutionContext } from '@nestjs/common';
|
|
17
|
+
import { Reflector } from '@nestjs/core';
|
|
18
|
+
import { AuthGuard } from '@nestjs/passport';
|
|
19
|
+
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
|
|
20
|
+
|
|
21
|
+
@Injectable()
|
|
22
|
+
export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
23
|
+
constructor(private readonly reflector: Reflector) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
canActivate(context: ExecutionContext): boolean | Promise<boolean> {
|
|
28
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
29
|
+
context.getHandler(),
|
|
30
|
+
context.getClass(),
|
|
31
|
+
]);
|
|
32
|
+
if (isPublic) return true;
|
|
33
|
+
// La llamada a super activa la estrategia Passport
|
|
34
|
+
return super.canActivate(context) as boolean | Promise<boolean>;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Registro global en app.module.ts o main.ts
|
|
39
|
+
app.useGlobalGuards(new JwtAuthGuard(reflector));
|
|
40
|
+
|
|
41
|
+
// Uso en controllers
|
|
42
|
+
@Public()
|
|
43
|
+
@Post('auth/login')
|
|
44
|
+
async login(@Body() dto: LoginDto): Promise<TokenResponseDto> {
|
|
45
|
+
return this.authService.login(dto);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## RolesGuard con @Roles() decorator
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// common/decorators/roles.decorator.ts
|
|
55
|
+
import { SetMetadata } from '@nestjs/common';
|
|
56
|
+
|
|
57
|
+
export type AppRole = 'ADMIN' | 'EDITOR' | 'LECTOR';
|
|
58
|
+
|
|
59
|
+
export const ROLES_KEY = 'roles';
|
|
60
|
+
export const Roles = (...roles: AppRole[]): MethodDecorator & ClassDecorator =>
|
|
61
|
+
SetMetadata(ROLES_KEY, roles);
|
|
62
|
+
|
|
63
|
+
// common/guards/roles.guard.ts
|
|
64
|
+
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
|
|
65
|
+
import { Reflector } from '@nestjs/core';
|
|
66
|
+
import { ROLES_KEY, AppRole } from '../decorators/roles.decorator';
|
|
67
|
+
|
|
68
|
+
interface AuthenticatedRequest extends Request {
|
|
69
|
+
user: { id: string; email: string; rol: AppRole };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Injectable()
|
|
73
|
+
export class RolesGuard implements CanActivate {
|
|
74
|
+
constructor(private readonly reflector: Reflector) {}
|
|
75
|
+
|
|
76
|
+
canActivate(context: ExecutionContext): boolean {
|
|
77
|
+
const rolesRequeridos = this.reflector.getAllAndOverride<AppRole[]>(
|
|
78
|
+
ROLES_KEY,
|
|
79
|
+
[context.getHandler(), context.getClass()],
|
|
80
|
+
);
|
|
81
|
+
if (!rolesRequeridos || rolesRequeridos.length === 0) return true;
|
|
82
|
+
|
|
83
|
+
const request = context.switchToHttp().getRequest<AuthenticatedRequest>();
|
|
84
|
+
const { user } = request;
|
|
85
|
+
|
|
86
|
+
if (!rolesRequeridos.includes(user.rol)) {
|
|
87
|
+
throw new ForbiddenException(
|
|
88
|
+
`Se requiere uno de los roles: ${rolesRequeridos.join(', ')}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Uso combinado en controller
|
|
96
|
+
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
97
|
+
@Roles('ADMIN')
|
|
98
|
+
@Delete(':id')
|
|
99
|
+
async eliminar(@Param('id', ParseUUIDPipe) id: string): Promise<void> {
|
|
100
|
+
return this.usersService.eliminar(id);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## LoggingInterceptor con tiempo de ejecución
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// common/interceptors/logging.interceptor.ts
|
|
110
|
+
import {
|
|
111
|
+
Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger,
|
|
112
|
+
} from '@nestjs/common';
|
|
113
|
+
import { Observable } from 'rxjs';
|
|
114
|
+
import { tap } from 'rxjs/operators';
|
|
115
|
+
|
|
116
|
+
@Injectable()
|
|
117
|
+
export class LoggingInterceptor implements NestInterceptor {
|
|
118
|
+
private readonly logger = new Logger(LoggingInterceptor.name);
|
|
119
|
+
|
|
120
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
|
121
|
+
const request = context.switchToHttp().getRequest<{
|
|
122
|
+
method: string;
|
|
123
|
+
url: string;
|
|
124
|
+
}>();
|
|
125
|
+
const { method, url } = request;
|
|
126
|
+
const inicio = Date.now();
|
|
127
|
+
|
|
128
|
+
return next.handle().pipe(
|
|
129
|
+
tap({
|
|
130
|
+
next: () => {
|
|
131
|
+
const duracionMs = Date.now() - inicio;
|
|
132
|
+
this.logger.log(`${method} ${url} — ${duracionMs}ms`);
|
|
133
|
+
},
|
|
134
|
+
error: (error: unknown) => {
|
|
135
|
+
const duracionMs = Date.now() - inicio;
|
|
136
|
+
const mensaje = error instanceof Error ? error.message : 'Error desconocido';
|
|
137
|
+
this.logger.error(`${method} ${url} — ${duracionMs}ms — ${mensaje}`);
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## TransformInterceptor — envolver responses en { data, metadata }
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// common/interceptors/transform.interceptor.ts
|
|
151
|
+
import {
|
|
152
|
+
Injectable, NestInterceptor, ExecutionContext, CallHandler,
|
|
153
|
+
} from '@nestjs/common';
|
|
154
|
+
import { Observable } from 'rxjs';
|
|
155
|
+
import { map } from 'rxjs/operators';
|
|
156
|
+
|
|
157
|
+
export interface ApiResponse<T> {
|
|
158
|
+
data: T;
|
|
159
|
+
timestamp: string;
|
|
160
|
+
path: string;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Injectable()
|
|
164
|
+
export class TransformInterceptor<T>
|
|
165
|
+
implements NestInterceptor<T, ApiResponse<T>>
|
|
166
|
+
{
|
|
167
|
+
intercept(
|
|
168
|
+
context: ExecutionContext,
|
|
169
|
+
next: CallHandler<T>,
|
|
170
|
+
): Observable<ApiResponse<T>> {
|
|
171
|
+
const request = context.switchToHttp().getRequest<{ url: string }>();
|
|
172
|
+
|
|
173
|
+
return next.handle().pipe(
|
|
174
|
+
map((data) => ({
|
|
175
|
+
data,
|
|
176
|
+
timestamp: new Date().toISOString(),
|
|
177
|
+
path: request.url,
|
|
178
|
+
})),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Registro global en main.ts
|
|
184
|
+
app.useGlobalInterceptors(new TransformInterceptor());
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## HttpExceptionFilter — formato de error estandarizado
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// common/filters/http-exception.filter.ts
|
|
193
|
+
import {
|
|
194
|
+
ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Logger,
|
|
195
|
+
} from '@nestjs/common';
|
|
196
|
+
import type { Request, Response } from 'express';
|
|
197
|
+
|
|
198
|
+
interface ErrorResponse {
|
|
199
|
+
statusCode: number;
|
|
200
|
+
timestamp: string;
|
|
201
|
+
path: string;
|
|
202
|
+
method: string;
|
|
203
|
+
message: string | string[];
|
|
204
|
+
error: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@Catch()
|
|
208
|
+
export class HttpExceptionFilter implements ExceptionFilter {
|
|
209
|
+
private readonly logger = new Logger(HttpExceptionFilter.name);
|
|
210
|
+
|
|
211
|
+
catch(exception: unknown, host: ArgumentsHost): void {
|
|
212
|
+
const ctx = host.switchToHttp();
|
|
213
|
+
const response = ctx.getResponse<Response>();
|
|
214
|
+
const request = ctx.getRequest<Request>();
|
|
215
|
+
|
|
216
|
+
let statusCode: number;
|
|
217
|
+
let message: string | string[];
|
|
218
|
+
let error: string;
|
|
219
|
+
|
|
220
|
+
if (exception instanceof HttpException) {
|
|
221
|
+
statusCode = exception.getStatus();
|
|
222
|
+
const responseBody = exception.getResponse();
|
|
223
|
+
if (typeof responseBody === 'object' && responseBody !== null) {
|
|
224
|
+
const body = responseBody as Record<string, unknown>;
|
|
225
|
+
message = (body['message'] as string | string[]) ?? exception.message;
|
|
226
|
+
error = (body['error'] as string) ?? exception.name;
|
|
227
|
+
} else {
|
|
228
|
+
message = exception.message;
|
|
229
|
+
error = exception.name;
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
233
|
+
message = 'Error interno del servidor';
|
|
234
|
+
error = 'InternalServerError';
|
|
235
|
+
// Log completo solo para errores no esperados
|
|
236
|
+
this.logger.error(
|
|
237
|
+
`Error no controlado: ${exception instanceof Error ? exception.message : String(exception)}`,
|
|
238
|
+
exception instanceof Error ? exception.stack : undefined,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const errorResponse: ErrorResponse = {
|
|
243
|
+
statusCode,
|
|
244
|
+
timestamp: new Date().toISOString(),
|
|
245
|
+
path: request.url,
|
|
246
|
+
method: request.method,
|
|
247
|
+
message,
|
|
248
|
+
error,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
response.status(statusCode).json(errorResponse);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Throttler guard — rate limiting
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Instalación: npm install @nestjs/throttler
|
|
262
|
+
// app.module.ts
|
|
263
|
+
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
|
|
264
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
265
|
+
|
|
266
|
+
@Module({
|
|
267
|
+
imports: [
|
|
268
|
+
ThrottlerModule.forRootAsync({
|
|
269
|
+
inject: [ConfigService],
|
|
270
|
+
useFactory: (config: ConfigService) => ([{
|
|
271
|
+
name: 'global',
|
|
272
|
+
ttl: config.get<number>('THROTTLE_TTL_MS') ?? 60_000,
|
|
273
|
+
limit: config.get<number>('THROTTLE_LIMIT') ?? 100,
|
|
274
|
+
}]),
|
|
275
|
+
}),
|
|
276
|
+
],
|
|
277
|
+
providers: [
|
|
278
|
+
// Guard global — aplica a todos los endpoints
|
|
279
|
+
{ provide: APP_GUARD, useClass: ThrottlerGuard },
|
|
280
|
+
],
|
|
281
|
+
})
|
|
282
|
+
export class AppModule {}
|
|
283
|
+
|
|
284
|
+
// Sobrescribir límite en un endpoint específico
|
|
285
|
+
import { Throttle, SkipThrottle } from '@nestjs/throttler';
|
|
286
|
+
|
|
287
|
+
@Throttle({ default: { ttl: 60_000, limit: 5 } })
|
|
288
|
+
@Post('auth/login')
|
|
289
|
+
async login(@Body() dto: LoginDto): Promise<TokenResponseDto> {
|
|
290
|
+
return this.authService.login(dto);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@SkipThrottle()
|
|
294
|
+
@Get('health')
|
|
295
|
+
health(): { status: string } {
|
|
296
|
+
return { status: 'ok' };
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## ValidationPipe avanzado con mensajes personalizados
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// common/pipes/validation.pipe.ts
|
|
306
|
+
import { ValidationPipe, UnprocessableEntityException } from '@nestjs/common';
|
|
307
|
+
import { ValidationError } from 'class-validator';
|
|
308
|
+
|
|
309
|
+
type FieldErrors = Record<string, string[]>;
|
|
310
|
+
|
|
311
|
+
function extraerErrores(errores: ValidationError[], prefijo = ''): FieldErrors {
|
|
312
|
+
return errores.reduce<FieldErrors>((acum, error) => {
|
|
313
|
+
const campo = prefijo ? `${prefijo}.${error.property}` : error.property;
|
|
314
|
+
|
|
315
|
+
if (error.constraints) {
|
|
316
|
+
acum[campo] = Object.values(error.constraints);
|
|
317
|
+
}
|
|
318
|
+
if (error.children && error.children.length > 0) {
|
|
319
|
+
Object.assign(acum, extraerErrores(error.children, campo));
|
|
320
|
+
}
|
|
321
|
+
return acum;
|
|
322
|
+
}, {});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export const globalValidationPipe = new ValidationPipe({
|
|
326
|
+
whitelist: true,
|
|
327
|
+
forbidNonWhitelisted: true,
|
|
328
|
+
transform: true,
|
|
329
|
+
transformOptions: { enableImplicitConversion: true },
|
|
330
|
+
exceptionFactory: (errores: ValidationError[]) => {
|
|
331
|
+
const campos = extraerErrores(errores);
|
|
332
|
+
return new UnprocessableEntityException({
|
|
333
|
+
message: 'Error de validación',
|
|
334
|
+
error: 'UnprocessableEntity',
|
|
335
|
+
campos,
|
|
336
|
+
});
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
```
|